Salome HOME
#17828 [CEA 17805] Polyhedron Mesh volume calculation and volume orientation criterion
[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     SMDS_VolumeTool vTool( aPolyedre );
1097     const int nbFaces = vTool.NbFaces();
1098     vector<int> quantities( nbFaces );
1099     vector<const SMDS_MeshNode *> poly_nodes;
1100
1101     // check if all facets are oriented equally
1102     bool sameOri = true;
1103     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1104     for (int iface = 0; iface < nbFaces; iface++)
1105     {
1106       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1107       if ( facetOri[ iface ] != facetOri[ 0 ])
1108         sameOri = false;
1109     }
1110
1111     // reverse faces of the polyhedron
1112     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1113     poly_nodes.reserve( vTool.NbNodes() );
1114     for ( int iface = 0; iface < nbFaces; iface++ )
1115     {
1116       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1117       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1118       bool toReverse = ( facetOri[ iface ] != neededOri );
1119
1120       quantities[ iface ] = nbFaceNodes;
1121
1122       if ( toReverse )
1123         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1124           poly_nodes.push_back( nodes[ inode ]);
1125       else
1126         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1127     }
1128     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1129   }
1130   else // other elements
1131   {
1132     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1133     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1134     if ( interlace.empty() )
1135     {
1136       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1137     }
1138     else
1139     {
1140       SMDS_MeshCell::applyInterlace( interlace, nodes );
1141     }
1142     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1143   }
1144   return false;
1145 }
1146
1147 //================================================================================
1148 /*!
1149  * \brief Reorient faces.
1150  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1151  * \param theDirection - desired direction of normal of \a theFace
1152  * \param theFace - one of \a theFaces that should be oriented according to
1153  *        \a theDirection and whose orientation defines orientation of other faces
1154  * \return number of reoriented faces.
1155  */
1156 //================================================================================
1157
1158 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1159                                   const gp_Dir&            theDirection,
1160                                   const SMDS_MeshElement * theFace)
1161 {
1162   int nbReori = 0;
1163   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1164
1165   if ( theFaces.empty() )
1166   {
1167     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1168     while ( fIt->more() )
1169       theFaces.insert( theFaces.end(), fIt->next() );
1170   }
1171
1172   // orient theFace according to theDirection
1173   gp_XYZ normal;
1174   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1175   if ( normal * theDirection.XYZ() < 0 )
1176     nbReori += Reorient( theFace );
1177
1178   // Orient other faces
1179
1180   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1181   TIDSortedElemSet avoidSet;
1182   set< SMESH_TLink > checkedLinks;
1183   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1184
1185   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1186     theFaces.erase( theFace );
1187   startFaces.insert( theFace );
1188
1189   int nodeInd1, nodeInd2;
1190   const SMDS_MeshElement*           otherFace;
1191   vector< const SMDS_MeshElement* > facesNearLink;
1192   vector< std::pair< int, int > >   nodeIndsOfFace;
1193
1194   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1195   while ( !startFaces.empty() )
1196   {
1197     startFace = startFaces.begin();
1198     theFace = *startFace;
1199     startFaces.erase( startFace );
1200     if ( !visitedFaces.insert( theFace ).second )
1201       continue;
1202
1203     avoidSet.clear();
1204     avoidSet.insert(theFace);
1205
1206     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1207
1208     const int nbNodes = theFace->NbCornerNodes();
1209     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1210     {
1211       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1212       linkIt_isNew = checkedLinks.insert( link );
1213       if ( !linkIt_isNew.second )
1214       {
1215         // link has already been checked and won't be encountered more
1216         // if the group (theFaces) is manifold
1217         //checkedLinks.erase( linkIt_isNew.first );
1218       }
1219       else
1220       {
1221         facesNearLink.clear();
1222         nodeIndsOfFace.clear();
1223         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1224                                                              theFaces, avoidSet,
1225                                                              &nodeInd1, &nodeInd2 )))
1226           if ( otherFace != theFace)
1227           {
1228             facesNearLink.push_back( otherFace );
1229             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1230             avoidSet.insert( otherFace );
1231           }
1232         if ( facesNearLink.size() > 1 )
1233         {
1234           // NON-MANIFOLD mesh shell !
1235           // select a face most co-directed with theFace,
1236           // other faces won't be visited this time
1237           gp_XYZ NF, NOF;
1238           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1239           double proj, maxProj = -1;
1240           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1241             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1242             if (( proj = Abs( NF * NOF )) > maxProj ) {
1243               maxProj = proj;
1244               otherFace = facesNearLink[i];
1245               nodeInd1  = nodeIndsOfFace[i].first;
1246               nodeInd2  = nodeIndsOfFace[i].second;
1247             }
1248           }
1249           // not to visit rejected faces
1250           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1251             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1252               visitedFaces.insert( facesNearLink[i] );
1253         }
1254         else if ( facesNearLink.size() == 1 )
1255         {
1256           otherFace = facesNearLink[0];
1257           nodeInd1  = nodeIndsOfFace.back().first;
1258           nodeInd2  = nodeIndsOfFace.back().second;
1259         }
1260         if ( otherFace && otherFace != theFace)
1261         {
1262           // link must be reverse in otherFace if orientation to otherFace
1263           // is same as that of theFace
1264           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1265           {
1266             nbReori += Reorient( otherFace );
1267           }
1268           startFaces.insert( otherFace );
1269         }
1270       }
1271       std::swap( link.first, link.second ); // reverse the link
1272     }
1273   }
1274   return nbReori;
1275 }
1276
1277 //================================================================================
1278 /*!
1279  * \brief Reorient faces basing on orientation of adjacent volumes.
1280  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1281  * \param theVolumes - reference volumes.
1282  * \param theOutsideNormal - to orient faces to have their normal
1283  *        pointing either \a outside or \a inside the adjacent volumes.
1284  * \return number of reoriented faces.
1285  */
1286 //================================================================================
1287
1288 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1289                                       TIDSortedElemSet & theVolumes,
1290                                       const bool         theOutsideNormal)
1291 {
1292   int nbReori = 0;
1293
1294   SMDS_ElemIteratorPtr faceIt;
1295   if ( theFaces.empty() )
1296     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1297   else
1298     faceIt = SMESHUtils::elemSetIterator( theFaces );
1299
1300   vector< const SMDS_MeshNode* > faceNodes;
1301   TIDSortedElemSet checkedVolumes;
1302   set< const SMDS_MeshNode* > faceNodesSet;
1303   SMDS_VolumeTool volumeTool;
1304
1305   while ( faceIt->more() ) // loop on given faces
1306   {
1307     const SMDS_MeshElement* face = faceIt->next();
1308     if ( face->GetType() != SMDSAbs_Face )
1309       continue;
1310
1311     const size_t nbCornersNodes = face->NbCornerNodes();
1312     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1313
1314     checkedVolumes.clear();
1315     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1316     while ( vIt->more() )
1317     {
1318       const SMDS_MeshElement* volume = vIt->next();
1319
1320       if ( !checkedVolumes.insert( volume ).second )
1321         continue;
1322       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1323         continue;
1324
1325       // is volume adjacent?
1326       bool allNodesCommon = true;
1327       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1328         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1329       if ( !allNodesCommon )
1330         continue;
1331
1332       // get nodes of a corresponding volume facet
1333       faceNodesSet.clear();
1334       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1335       volumeTool.Set( volume );
1336       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1337       if ( facetID < 0 ) continue;
1338       volumeTool.SetExternalNormal();
1339       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1340
1341       // compare order of faceNodes and facetNodes
1342       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1343       int iNN[2];
1344       for ( int i = 0; i < 2; ++i )
1345       {
1346         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1347         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1348           if ( faceNodes[ iN ] == n )
1349           {
1350             iNN[ i ] = iN;
1351             break;
1352           }
1353       }
1354       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1355       if ( isOutside != theOutsideNormal )
1356         nbReori += Reorient( face );
1357     }
1358   }  // loop on given faces
1359
1360   return nbReori;
1361 }
1362
1363 //=======================================================================
1364 //function : getBadRate
1365 //purpose  :
1366 //=======================================================================
1367
1368 static double getBadRate (const SMDS_MeshElement*               theElem,
1369                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1370 {
1371   SMESH::Controls::TSequenceOfXYZ P;
1372   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1373     return 1e100;
1374   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1375   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1376 }
1377
1378 //=======================================================================
1379 //function : QuadToTri
1380 //purpose  : Cut quadrangles into triangles.
1381 //           theCrit is used to select a diagonal to cut
1382 //=======================================================================
1383
1384 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1385                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1386 {
1387   ClearLastCreated();
1388
1389   if ( !theCrit.get() )
1390     return false;
1391
1392   SMESHDS_Mesh *       aMesh = GetMeshDS();
1393   Handle(Geom_Surface) surface;
1394   SMESH_MesherHelper   helper( *GetMesh() );
1395
1396   myLastCreatedElems.reserve( theElems.size() * 2 );
1397
1398   TIDSortedElemSet::iterator itElem;
1399   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1400   {
1401     const SMDS_MeshElement* elem = *itElem;
1402     if ( !elem || elem->GetType() != SMDSAbs_Face )
1403       continue;
1404     if ( elem->NbCornerNodes() != 4 )
1405       continue;
1406
1407     // retrieve element nodes
1408     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1409
1410     // compare two sets of possible triangles
1411     double aBadRate1, aBadRate2; // to what extent a set is bad
1412     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1413     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1414     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1415
1416     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1417     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1418     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1419
1420     const int aShapeId = FindShape( elem );
1421     const SMDS_MeshElement* newElem1 = 0;
1422     const SMDS_MeshElement* newElem2 = 0;
1423
1424     if ( !elem->IsQuadratic() ) // split linear quadrangle
1425     {
1426       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1427       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1428       if ( aBadRate1 <= aBadRate2 ) {
1429         // tr1 + tr2 is better
1430         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1431         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1432       }
1433       else {
1434         // tr3 + tr4 is better
1435         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1436         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1437       }
1438     }
1439     else // split quadratic quadrangle
1440     {
1441       helper.SetIsQuadratic( true );
1442       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1443
1444       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1445       if ( aNodes.size() == 9 )
1446       {
1447         helper.SetIsBiQuadratic( true );
1448         if ( aBadRate1 <= aBadRate2 )
1449           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1450         else
1451           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1452       }
1453       // create a new element
1454       if ( aBadRate1 <= aBadRate2 ) {
1455         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1456         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1457       }
1458       else {
1459         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1460         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1461       }
1462     } // quadratic case
1463
1464     // care of a new element
1465
1466     myLastCreatedElems.push_back(newElem1);
1467     myLastCreatedElems.push_back(newElem2);
1468     AddToSameGroups( newElem1, elem, aMesh );
1469     AddToSameGroups( newElem2, elem, aMesh );
1470
1471     // put a new triangle on the same shape
1472     if ( aShapeId )
1473       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1474     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1475
1476     aMesh->RemoveElement( elem );
1477   }
1478   return true;
1479 }
1480
1481 //=======================================================================
1482 /*!
1483  * \brief Split each of given quadrangles into 4 triangles.
1484  * \param theElems - The faces to be split. If empty all faces are split.
1485  */
1486 //=======================================================================
1487
1488 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1489 {
1490   ClearLastCreated();
1491   myLastCreatedElems.reserve( theElems.size() * 4 );
1492
1493   SMESH_MesherHelper helper( *GetMesh() );
1494   helper.SetElementsOnShape( true );
1495
1496   SMDS_ElemIteratorPtr faceIt;
1497   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1498   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1499
1500   bool   checkUV;
1501   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1502   gp_XYZ xyz[9];
1503   vector< const SMDS_MeshNode* > nodes;
1504   SMESHDS_SubMesh*               subMeshDS = 0;
1505   TopoDS_Face                    F;
1506   Handle(Geom_Surface)           surface;
1507   TopLoc_Location                loc;
1508
1509   while ( faceIt->more() )
1510   {
1511     const SMDS_MeshElement* quad = faceIt->next();
1512     if ( !quad || quad->NbCornerNodes() != 4 )
1513       continue;
1514
1515     // get a surface the quad is on
1516
1517     if ( quad->getshapeId() < 1 )
1518     {
1519       F.Nullify();
1520       helper.SetSubShape( 0 );
1521       subMeshDS = 0;
1522     }
1523     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1524     {
1525       helper.SetSubShape( quad->getshapeId() );
1526       if ( !helper.GetSubShape().IsNull() &&
1527            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1528       {
1529         F = TopoDS::Face( helper.GetSubShape() );
1530         surface = BRep_Tool::Surface( F, loc );
1531         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1532       }
1533       else
1534       {
1535         helper.SetSubShape( 0 );
1536         subMeshDS = 0;
1537       }
1538     }
1539
1540     // create a central node
1541
1542     const SMDS_MeshNode* nCentral;
1543     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1544
1545     if ( nodes.size() == 9 )
1546     {
1547       nCentral = nodes.back();
1548     }
1549     else
1550     {
1551       size_t iN = 0;
1552       if ( F.IsNull() )
1553       {
1554         for ( ; iN < nodes.size(); ++iN )
1555           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1556
1557         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1558           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1559
1560         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1561                                    xyz[0], xyz[1], xyz[2], xyz[3],
1562                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1563       }
1564       else
1565       {
1566         for ( ; iN < nodes.size(); ++iN )
1567           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1568
1569         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1570           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1571
1572         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1573                                   uv[0], uv[1], uv[2], uv[3],
1574                                   uv[4], uv[5], uv[6], uv[7] );
1575
1576         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1577         xyz[ 8 ] = p.XYZ();
1578       }
1579
1580       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1581                                  uv[8].X(), uv[8].Y() );
1582       myLastCreatedNodes.push_back( nCentral );
1583     }
1584
1585     // create 4 triangles
1586
1587     helper.SetIsQuadratic  ( nodes.size() > 4 );
1588     helper.SetIsBiQuadratic( nodes.size() == 9 );
1589     if ( helper.GetIsQuadratic() )
1590       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1591
1592     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1593
1594     for ( int i = 0; i < 4; ++i )
1595     {
1596       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1597                                                nodes[(i+1)%4],
1598                                                nCentral );
1599       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1600       myLastCreatedElems.push_back( tria );
1601     }
1602   }
1603 }
1604
1605 //=======================================================================
1606 //function : BestSplit
1607 //purpose  : Find better diagonal for cutting.
1608 //=======================================================================
1609
1610 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1611                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1612 {
1613   ClearLastCreated();
1614
1615   if (!theCrit.get())
1616     return -1;
1617
1618   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1619     return -1;
1620
1621   if( theQuad->NbNodes()==4 ||
1622       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1623
1624     // retrieve element nodes
1625     const SMDS_MeshNode* aNodes [4];
1626     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1627     int i = 0;
1628     //while (itN->more())
1629     while (i<4) {
1630       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1631     }
1632     // compare two sets of possible triangles
1633     double aBadRate1, aBadRate2; // to what extent a set is bad
1634     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1635     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1636     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1637
1638     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1639     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1640     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1641     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1642     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1643     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1644       return 1; // diagonal 1-3
1645
1646     return 2; // diagonal 2-4
1647   }
1648   return -1;
1649 }
1650
1651 namespace
1652 {
1653   // Methods of splitting volumes into tetra
1654
1655   const int theHexTo5_1[5*4+1] =
1656     {
1657       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1658     };
1659   const int theHexTo5_2[5*4+1] =
1660     {
1661       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1662     };
1663   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1664
1665   const int theHexTo6_1[6*4+1] =
1666     {
1667       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
1668     };
1669   const int theHexTo6_2[6*4+1] =
1670     {
1671       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
1672     };
1673   const int theHexTo6_3[6*4+1] =
1674     {
1675       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
1676     };
1677   const int theHexTo6_4[6*4+1] =
1678     {
1679       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
1680     };
1681   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1682
1683   const int thePyraTo2_1[2*4+1] =
1684     {
1685       0, 1, 2, 4,    0, 2, 3, 4,   -1
1686     };
1687   const int thePyraTo2_2[2*4+1] =
1688     {
1689       1, 2, 3, 4,    1, 3, 0, 4,   -1
1690     };
1691   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1692
1693   const int thePentaTo3_1[3*4+1] =
1694     {
1695       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1696     };
1697   const int thePentaTo3_2[3*4+1] =
1698     {
1699       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1700     };
1701   const int thePentaTo3_3[3*4+1] =
1702     {
1703       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1704     };
1705   const int thePentaTo3_4[3*4+1] =
1706     {
1707       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1708     };
1709   const int thePentaTo3_5[3*4+1] =
1710     {
1711       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1712     };
1713   const int thePentaTo3_6[3*4+1] =
1714     {
1715       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1716     };
1717   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1718                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1719
1720   // Methods of splitting hexahedron into prisms
1721
1722   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1723     {
1724       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
1725     };
1726   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1727     {
1728       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
1729     };
1730   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1731     {
1732       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
1733     };
1734
1735   const int theHexTo2Prisms_BT_1[6*2+1] =
1736     {
1737       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1738     };
1739   const int theHexTo2Prisms_BT_2[6*2+1] =
1740     {
1741       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1742     };
1743   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1744
1745   const int theHexTo2Prisms_LR_1[6*2+1] =
1746     {
1747       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1748     };
1749   const int theHexTo2Prisms_LR_2[6*2+1] =
1750     {
1751       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1752     };
1753   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1754
1755   const int theHexTo2Prisms_FB_1[6*2+1] =
1756     {
1757       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1758     };
1759   const int theHexTo2Prisms_FB_2[6*2+1] =
1760     {
1761       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1762     };
1763   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1764
1765
1766   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1767   {
1768     int _n1, _n2, _n3;
1769     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1770     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1771     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1772                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1773   };
1774   struct TSplitMethod
1775   {
1776     int        _nbSplits;
1777     int        _nbCorners;
1778     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1779     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1780     bool       _ownConn;      //!< to delete _connectivity in destructor
1781     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1782
1783     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1784       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1785     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1786     bool hasFacet( const TTriangleFacet& facet ) const
1787     {
1788       if ( _nbCorners == 4 )
1789       {
1790         const int* tetConn = _connectivity;
1791         for ( ; tetConn[0] >= 0; tetConn += 4 )
1792           if (( facet.contains( tetConn[0] ) +
1793                 facet.contains( tetConn[1] ) +
1794                 facet.contains( tetConn[2] ) +
1795                 facet.contains( tetConn[3] )) == 3 )
1796             return true;
1797       }
1798       else // prism, _nbCorners == 6
1799       {
1800         const int* prismConn = _connectivity;
1801         for ( ; prismConn[0] >= 0; prismConn += 6 )
1802         {
1803           if (( facet.contains( prismConn[0] ) &&
1804                 facet.contains( prismConn[1] ) &&
1805                 facet.contains( prismConn[2] ))
1806               ||
1807               ( facet.contains( prismConn[3] ) &&
1808                 facet.contains( prismConn[4] ) &&
1809                 facet.contains( prismConn[5] )))
1810             return true;
1811         }
1812       }
1813       return false;
1814     }
1815   };
1816
1817   //=======================================================================
1818   /*!
1819    * \brief return TSplitMethod for the given element to split into tetrahedra
1820    */
1821   //=======================================================================
1822
1823   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1824   {
1825     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1826
1827     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1828     // an edge and a face barycenter; tertaherdons are based on triangles and
1829     // a volume barycenter
1830     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1831
1832     // Find out how adjacent volumes are split
1833
1834     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1835     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1836     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1837     {
1838       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1839       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1840       if ( nbNodes < 4 ) continue;
1841
1842       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1843       const int* nInd = vol.GetFaceNodesIndices( iF );
1844       if ( nbNodes == 4 )
1845       {
1846         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1847         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1848         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1849         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1850       }
1851       else
1852       {
1853         int iCom = 0; // common node of triangle faces to split into
1854         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1855         {
1856           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1857                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1858                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1859           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1861                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1862           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1863           {
1864             triaSplits.push_back( t012 );
1865             triaSplits.push_back( t023 );
1866             break;
1867           }
1868         }
1869       }
1870       if ( !triaSplits.empty() )
1871         hasAdjacentSplits = true;
1872     }
1873
1874     // Among variants of split method select one compliant with adjacent volumes
1875
1876     TSplitMethod method;
1877     if ( !vol.Element()->IsPoly() && !is24TetMode )
1878     {
1879       int nbVariants = 2, nbTet = 0;
1880       const int** connVariants = 0;
1881       switch ( vol.Element()->GetEntityType() )
1882       {
1883       case SMDSEntity_Hexa:
1884       case SMDSEntity_Quad_Hexa:
1885       case SMDSEntity_TriQuad_Hexa:
1886         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1887           connVariants = theHexTo5, nbTet = 5;
1888         else
1889           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1890         break;
1891       case SMDSEntity_Pyramid:
1892       case SMDSEntity_Quad_Pyramid:
1893         connVariants = thePyraTo2;  nbTet = 2;
1894         break;
1895       case SMDSEntity_Penta:
1896       case SMDSEntity_Quad_Penta:
1897       case SMDSEntity_BiQuad_Penta:
1898         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1899         break;
1900       default:
1901         nbVariants = 0;
1902       }
1903       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1904       {
1905         // check method compliancy with adjacent tetras,
1906         // all found splits must be among facets of tetras described by this method
1907         method = TSplitMethod( nbTet, connVariants[variant] );
1908         if ( hasAdjacentSplits && method._nbSplits > 0 )
1909         {
1910           bool facetCreated = true;
1911           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1912           {
1913             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1914             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1915               facetCreated = method.hasFacet( *facet );
1916           }
1917           if ( !facetCreated )
1918             method = TSplitMethod(0); // incompatible method
1919         }
1920       }
1921     }
1922     if ( method._nbSplits < 1 )
1923     {
1924       // No standard method is applicable, use a generic solution:
1925       // each facet of a volume is split into triangles and
1926       // each of triangles and a volume barycenter form a tetrahedron.
1927
1928       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1929
1930       int* connectivity = new int[ maxTetConnSize + 1 ];
1931       method._connectivity = connectivity;
1932       method._ownConn = true;
1933       method._baryNode = !isHex27; // to create central node or not
1934
1935       int connSize = 0;
1936       int baryCenInd = vol.NbNodes() - int( isHex27 );
1937       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1938       {
1939         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1940         const int*   nInd = vol.GetFaceNodesIndices( iF );
1941         // find common node of triangle facets of tetra to create
1942         int iCommon = 0; // index in linear numeration
1943         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1944         if ( !triaSplits.empty() )
1945         {
1946           // by found facets
1947           const TTriangleFacet* facet = &triaSplits.front();
1948           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1949             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1950                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1951               break;
1952         }
1953         else if ( nbNodes > 3 && !is24TetMode )
1954         {
1955           // find the best method of splitting into triangles by aspect ratio
1956           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1957           map< double, int > badness2iCommon;
1958           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1959           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1960           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1961           {
1962             double badness = 0;
1963             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1964             {
1965               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1966                                       nodes[ iQ*((iLast-1)%nbNodes)],
1967                                       nodes[ iQ*((iLast  )%nbNodes)]);
1968               badness += getBadRate( &tria, aspectRatio );
1969             }
1970             badness2iCommon.insert( make_pair( badness, iCommon ));
1971           }
1972           // use iCommon with lowest badness
1973           iCommon = badness2iCommon.begin()->second;
1974         }
1975         if ( iCommon >= nbNodes )
1976           iCommon = 0; // something wrong
1977
1978         // fill connectivity of tetrahedra based on a current face
1979         int nbTet = nbNodes - 2;
1980         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1981         {
1982           int faceBaryCenInd;
1983           if ( isHex27 )
1984           {
1985             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1986             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1987           }
1988           else
1989           {
1990             method._faceBaryNode[ iF ] = 0;
1991             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1992           }
1993           nbTet = nbNodes;
1994           for ( int i = 0; i < nbTet; ++i )
1995           {
1996             int i1 = i, i2 = (i+1) % nbNodes;
1997             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1998             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1999             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2000             connectivity[ connSize++ ] = faceBaryCenInd;
2001             connectivity[ connSize++ ] = baryCenInd;
2002           }
2003         }
2004         else
2005         {
2006           for ( int i = 0; i < nbTet; ++i )
2007           {
2008             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2009             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2010             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2011             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2012             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2013             connectivity[ connSize++ ] = baryCenInd;
2014           }
2015         }
2016         method._nbSplits += nbTet;
2017
2018       } // loop on volume faces
2019
2020       connectivity[ connSize++ ] = -1;
2021
2022     } // end of generic solution
2023
2024     return method;
2025   }
2026   //=======================================================================
2027   /*!
2028    * \brief return TSplitMethod to split haxhedron into prisms
2029    */
2030   //=======================================================================
2031
2032   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2033                                     const int        methodFlags,
2034                                     const int        facetToSplit)
2035   {
2036     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2037     // B, T, L, B, R, F
2038     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2039
2040     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2041     {
2042       static TSplitMethod to4methods[4]; // order BT, LR, FB
2043       if ( to4methods[iF]._nbSplits == 0 )
2044       {
2045         switch ( iF ) {
2046         case 0:
2047           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2048           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2049           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2050           break;
2051         case 1:
2052           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2053           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2054           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2055           break;
2056         case 2:
2057           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2058           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2059           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2060           break;
2061         default: return to4methods[3];
2062         }
2063         to4methods[iF]._nbSplits  = 4;
2064         to4methods[iF]._nbCorners = 6;
2065       }
2066       return to4methods[iF];
2067     }
2068     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2069
2070     TSplitMethod method;
2071
2072     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2073
2074     const int nbVariants = 2, nbSplits = 2;
2075     const int** connVariants = 0;
2076     switch ( iF ) {
2077     case 0: connVariants = theHexTo2Prisms_BT; break;
2078     case 1: connVariants = theHexTo2Prisms_LR; break;
2079     case 2: connVariants = theHexTo2Prisms_FB; break;
2080     default: return method;
2081     }
2082
2083     // look for prisms adjacent via facetToSplit and an opposite one
2084     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2085     {
2086       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2087       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2088       if ( nbNodes != 4 ) return method;
2089
2090       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2091       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2092       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2093       TTriangleFacet* t;
2094       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2095         t = &t012;
2096       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097         t = &t123;
2098       else
2099         continue;
2100
2101       // there are adjacent prism
2102       for ( int variant = 0; variant < nbVariants; ++variant )
2103       {
2104         // check method compliancy with adjacent prisms,
2105         // the found prism facets must be among facets of prisms described by current method
2106         method._nbSplits     = nbSplits;
2107         method._nbCorners    = 6;
2108         method._connectivity = connVariants[ variant ];
2109         if ( method.hasFacet( *t ))
2110           return method;
2111       }
2112     }
2113
2114     // No adjacent prisms. Select a variant with a best aspect ratio.
2115
2116     double badness[2] = { 0., 0. };
2117     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2118     const SMDS_MeshNode** nodes = vol.GetNodes();
2119     for ( int variant = 0; variant < nbVariants; ++variant )
2120       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2121       {
2122         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2123         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2124
2125         method._connectivity = connVariants[ variant ];
2126         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2127         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2128         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2129
2130         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2131                                 nodes[ t->_n2 ],
2132                                 nodes[ t->_n3 ] );
2133         badness[ variant ] += getBadRate( &tria, aspectRatio );
2134       }
2135     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2136
2137     method._nbSplits     = nbSplits;
2138     method._nbCorners    = 6;
2139     method._connectivity = connVariants[ iBetter ];
2140
2141     return method;
2142   }
2143
2144   //================================================================================
2145   /*!
2146    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2147    */
2148   //================================================================================
2149
2150   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2151                                        const SMDSAbs_GeometryType geom ) const
2152   {
2153     // find the tetrahedron including the three nodes of facet
2154     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2155     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2156     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2157     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2158     while ( volIt1->more() )
2159     {
2160       const SMDS_MeshElement* v = volIt1->next();
2161       if ( v->GetGeomType() != geom )
2162         continue;
2163       const int lastCornerInd = v->NbCornerNodes() - 1;
2164       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2165         continue; // medium node not allowed
2166       const int ind2 = v->GetNodeIndex( n2 );
2167       if ( ind2 < 0 || lastCornerInd < ind2 )
2168         continue;
2169       const int ind3 = v->GetNodeIndex( n3 );
2170       if ( ind3 < 0 || lastCornerInd < ind3 )
2171         continue;
2172       return true;
2173     }
2174     return false;
2175   }
2176
2177   //=======================================================================
2178   /*!
2179    * \brief A key of a face of volume
2180    */
2181   //=======================================================================
2182
2183   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2184   {
2185     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2186     {
2187       TIDSortedNodeSet sortedNodes;
2188       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2189       int nbNodes = vol.NbFaceNodes( iF );
2190       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2191       for ( int i = 0; i < nbNodes; i += iQ )
2192         sortedNodes.insert( fNodes[i] );
2193       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2194       first.first   = (*(n++))->GetID();
2195       first.second  = (*(n++))->GetID();
2196       second.first  = (*(n++))->GetID();
2197       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2198     }
2199   };
2200 } // namespace
2201
2202 //=======================================================================
2203 //function : SplitVolumes
2204 //purpose  : Split volume elements into tetrahedra or prisms.
2205 //           If facet ID < 0, element is split into tetrahedra,
2206 //           else a hexahedron is split into prisms so that the given facet is
2207 //           split into triangles
2208 //=======================================================================
2209
2210 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2211                                      const int            theMethodFlags)
2212 {
2213   SMDS_VolumeTool    volTool;
2214   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2215   fHelper.ToFixNodeParameters( true );
2216
2217   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2218   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2219
2220   SMESH_SequenceOfElemPtr newNodes, newElems;
2221
2222   // map face of volume to it's baricenrtic node
2223   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2224   double bc[3];
2225   vector<const SMDS_MeshElement* > splitVols;
2226
2227   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2228   for ( ; elem2facet != theElems.end(); ++elem2facet )
2229   {
2230     const SMDS_MeshElement* elem = elem2facet->first;
2231     const int       facetToSplit = elem2facet->second;
2232     if ( elem->GetType() != SMDSAbs_Volume )
2233       continue;
2234     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2235     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2236       continue;
2237
2238     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2239
2240     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2241                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2242                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2243     if ( splitMethod._nbSplits < 1 ) continue;
2244
2245     // find submesh to add new tetras to
2246     if ( !subMesh || !subMesh->Contains( elem ))
2247     {
2248       int shapeID = FindShape( elem );
2249       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2250       subMesh = GetMeshDS()->MeshElements( shapeID );
2251     }
2252     int iQ;
2253     if ( elem->IsQuadratic() )
2254     {
2255       iQ = 2;
2256       // add quadratic links to the helper
2257       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2258       {
2259         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2260         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2261         for ( int iN = 0; iN < nbN; iN += iQ )
2262           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2263       }
2264       helper.SetIsQuadratic( true );
2265     }
2266     else
2267     {
2268       iQ = 1;
2269       helper.SetIsQuadratic( false );
2270     }
2271     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2272                                         volTool.GetNodes() + elem->NbNodes() );
2273     helper.SetElementsOnShape( true );
2274     if ( splitMethod._baryNode )
2275     {
2276       // make a node at barycenter
2277       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2278       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2279       nodes.push_back( gcNode );
2280       newNodes.push_back( gcNode );
2281     }
2282     if ( !splitMethod._faceBaryNode.empty() )
2283     {
2284       // make or find baricentric nodes of faces
2285       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2286       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2287       {
2288         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2289           volFace2BaryNode.insert
2290           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2291         if ( !f_n->second )
2292         {
2293           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2294           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2295         }
2296         nodes.push_back( iF_n->second = f_n->second );
2297       }
2298     }
2299
2300     // make new volumes
2301     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2302     const int* volConn = splitMethod._connectivity;
2303     if ( splitMethod._nbCorners == 4 ) // tetra
2304       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2305         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2306                                                                nodes[ volConn[1] ],
2307                                                                nodes[ volConn[2] ],
2308                                                                nodes[ volConn[3] ]));
2309     else // prisms
2310       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2311         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2312                                                                nodes[ volConn[1] ],
2313                                                                nodes[ volConn[2] ],
2314                                                                nodes[ volConn[3] ],
2315                                                                nodes[ volConn[4] ],
2316                                                                nodes[ volConn[5] ]));
2317
2318     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2319
2320     // Split faces on sides of the split volume
2321
2322     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2323     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2324     {
2325       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2326       if ( nbNodes < 4 ) continue;
2327
2328       // find an existing face
2329       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2330                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2331       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2332                                                                        /*noMedium=*/false))
2333       {
2334         // make triangles
2335         helper.SetElementsOnShape( false );
2336         vector< const SMDS_MeshElement* > triangles;
2337
2338         // find submesh to add new triangles in
2339         if ( !fSubMesh || !fSubMesh->Contains( face ))
2340         {
2341           int shapeID = FindShape( face );
2342           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2343         }
2344         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2345         if ( iF_n != splitMethod._faceBaryNode.end() )
2346         {
2347           const SMDS_MeshNode *baryNode = iF_n->second;
2348           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2349           {
2350             const SMDS_MeshNode* n1 = fNodes[iN];
2351             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2352             const SMDS_MeshNode *n3 = baryNode;
2353             if ( !volTool.IsFaceExternal( iF ))
2354               swap( n2, n3 );
2355             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2356           }
2357           if ( fSubMesh ) // update position of the bary node on geometry
2358           {
2359             if ( subMesh )
2360               subMesh->RemoveNode( baryNode );
2361             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2362             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2363             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2364             {
2365               fHelper.SetSubShape( s );
2366               gp_XY uv( 1e100, 1e100 );
2367               double distXYZ[4];
2368               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2369                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2370                    uv.X() < 1e100 )
2371               {
2372                 // node is too far from the surface
2373                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2374                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2375                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2376               }
2377             }
2378           }
2379         }
2380         else
2381         {
2382           // among possible triangles create ones described by split method
2383           const int* nInd = volTool.GetFaceNodesIndices( iF );
2384           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2385           int iCom = 0; // common node of triangle faces to split into
2386           list< TTriangleFacet > facets;
2387           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2388           {
2389             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2390                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2391                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2392             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2393                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2394                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2395             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2396             {
2397               facets.push_back( t012 );
2398               facets.push_back( t023 );
2399               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2400                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2401                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2402                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2403               break;
2404             }
2405           }
2406           list< TTriangleFacet >::iterator facet = facets.begin();
2407           if ( facet == facets.end() )
2408             break;
2409           for ( ; facet != facets.end(); ++facet )
2410           {
2411             if ( !volTool.IsFaceExternal( iF ))
2412               swap( facet->_n2, facet->_n3 );
2413             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2414                                                  volNodes[ facet->_n2 ],
2415                                                  volNodes[ facet->_n3 ]));
2416           }
2417         }
2418         for ( size_t i = 0; i < triangles.size(); ++i )
2419         {
2420           if ( !triangles[ i ]) continue;
2421           if ( fSubMesh )
2422             fSubMesh->AddElement( triangles[ i ]);
2423           newElems.push_back( triangles[ i ]);
2424         }
2425         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2426         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2427
2428       } // while a face based on facet nodes exists
2429     } // loop on volume faces to split them into triangles
2430
2431     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2432
2433     if ( geomType == SMDSEntity_TriQuad_Hexa )
2434     {
2435       // remove medium nodes that could become free
2436       for ( int i = 20; i < volTool.NbNodes(); ++i )
2437         if ( volNodes[i]->NbInverseElements() == 0 )
2438           GetMeshDS()->RemoveNode( volNodes[i] );
2439     }
2440   } // loop on volumes to split
2441
2442   myLastCreatedNodes = newNodes;
2443   myLastCreatedElems = newElems;
2444 }
2445
2446 //=======================================================================
2447 //function : GetHexaFacetsToSplit
2448 //purpose  : For hexahedra that will be split into prisms, finds facets to
2449 //           split into triangles. Only hexahedra adjacent to the one closest
2450 //           to theFacetNormal.Location() are returned.
2451 //param [in,out] theHexas - the hexahedra
2452 //param [in]     theFacetNormal - facet normal
2453 //param [out]    theFacets - the hexahedra and found facet IDs
2454 //=======================================================================
2455
2456 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2457                                              const gp_Ax1&     theFacetNormal,
2458                                              TFacetOfElem &    theFacets)
2459 {
2460 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2461
2462   // Find a hexa closest to the location of theFacetNormal
2463
2464   const SMDS_MeshElement* startHex;
2465   {
2466     // get SMDS_ElemIteratorPtr on theHexas
2467     typedef const SMDS_MeshElement*                                      TValue;
2468     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2469     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2470     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2471     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2472     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2473       ( new TElemSetIter( theHexas.begin(),
2474                           theHexas.end(),
2475                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2476
2477     SMESH_ElementSearcher* searcher =
2478       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2479
2480     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2481
2482     delete searcher;
2483
2484     if ( !startHex )
2485       throw SALOME_Exception( THIS_METHOD "startHex not found");
2486   }
2487
2488   // Select a facet of startHex by theFacetNormal
2489
2490   SMDS_VolumeTool vTool( startHex );
2491   double norm[3], dot, maxDot = 0;
2492   int facetID = -1;
2493   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2494     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2495     {
2496       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2497       if ( dot > maxDot )
2498       {
2499         facetID = iF;
2500         maxDot = dot;
2501       }
2502     }
2503   if ( facetID < 0 )
2504     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2505
2506   // Fill theFacets starting from facetID of startHex
2507
2508   // facets used for searching of volumes adjacent to already treated ones
2509   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2510   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2511   TFacetMap facetsToCheck;
2512
2513   set<const SMDS_MeshNode*> facetNodes;
2514   const SMDS_MeshElement*   curHex;
2515
2516   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2517
2518   while ( startHex )
2519   {
2520     // move in two directions from startHex via facetID
2521     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2522     {
2523       curHex       = startHex;
2524       int curFacet = facetID;
2525       if ( is2nd ) // do not treat startHex twice
2526       {
2527         vTool.Set( curHex );
2528         if ( vTool.IsFreeFace( curFacet, &curHex ))
2529         {
2530           curHex = 0;
2531         }
2532         else
2533         {
2534           vTool.GetFaceNodes( curFacet, facetNodes );
2535           vTool.Set( curHex );
2536           curFacet = vTool.GetFaceIndex( facetNodes );
2537         }
2538       }
2539       while ( curHex )
2540       {
2541         // store a facet to split
2542         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2543         {
2544           theFacets.insert( make_pair( curHex, -1 ));
2545           break;
2546         }
2547         if ( !allHex && !theHexas.count( curHex ))
2548           break;
2549
2550         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2551           theFacets.insert( make_pair( curHex, curFacet ));
2552         if ( !facetIt2isNew.second )
2553           break;
2554
2555         // remember not-to-split facets in facetsToCheck
2556         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2557         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2558         {
2559           if ( iF == curFacet && iF == oppFacet )
2560             continue;
2561           TVolumeFaceKey facetKey ( vTool, iF );
2562           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2563           pair< TFacetMap::iterator, bool > it2isnew =
2564             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2565           if ( !it2isnew.second )
2566             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2567         }
2568         // pass to a volume adjacent via oppFacet
2569         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2570         {
2571           curHex = 0;
2572         }
2573         else
2574         {
2575           // get a new curFacet
2576           vTool.GetFaceNodes( oppFacet, facetNodes );
2577           vTool.Set( curHex );
2578           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2579         }
2580       }
2581     } // move in two directions from startHex via facetID
2582
2583     // Find a new startHex by facetsToCheck
2584
2585     startHex = 0;
2586     facetID  = -1;
2587     TFacetMap::iterator fIt = facetsToCheck.begin();
2588     while ( !startHex && fIt != facetsToCheck.end() )
2589     {
2590       const TElemFacets&  elemFacets = fIt->second;
2591       const SMDS_MeshElement*    hex = elemFacets.first->first;
2592       int                 splitFacet = elemFacets.first->second;
2593       int               lateralFacet = elemFacets.second;
2594       facetsToCheck.erase( fIt );
2595       fIt = facetsToCheck.begin();
2596
2597       vTool.Set( hex );
2598       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2599            curHex->GetGeomType() != SMDSGeom_HEXA )
2600         continue;
2601       if ( !allHex && !theHexas.count( curHex ))
2602         continue;
2603
2604       startHex = curHex;
2605
2606       // find a facet of startHex to split
2607
2608       set<const SMDS_MeshNode*> lateralNodes;
2609       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2610       vTool.GetFaceNodes( splitFacet,   facetNodes );
2611       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2612       vTool.Set( startHex );
2613       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2614
2615       // look for a facet of startHex having common nodes with facetNodes
2616       // but not lateralFacet
2617       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2618       {
2619         if ( iF == lateralFacet )
2620           continue;
2621         int nbCommonNodes = 0;
2622         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2623         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2624           nbCommonNodes += facetNodes.count( nn[ iN ]);
2625
2626         if ( nbCommonNodes >= 2 )
2627         {
2628           facetID = iF;
2629           break;
2630         }
2631       }
2632       if ( facetID < 0 )
2633         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2634     }
2635   } //   while ( startHex )
2636
2637   return;
2638 }
2639
2640 namespace
2641 {
2642   //================================================================================
2643   /*!
2644    * \brief Selects nodes of several elements according to a given interlace
2645    *  \param [in] srcNodes - nodes to select from
2646    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2647    *  \param [in] interlace - indices of nodes for all elements
2648    *  \param [in] nbElems - nb of elements
2649    *  \param [in] nbNodes - nb of nodes in each element
2650    *  \param [in] mesh - the mesh
2651    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2652    *  \param [in] type - type of elements to look for
2653    */
2654   //================================================================================
2655
2656   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2657                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2658                     const int*                            interlace,
2659                     const int                             nbElems,
2660                     const int                             nbNodes,
2661                     SMESHDS_Mesh*                         mesh = 0,
2662                     list< const SMDS_MeshElement* >*      elemQueue=0,
2663                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2664   {
2665     for ( int iE = 0; iE < nbElems; ++iE )
2666     {
2667       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2668       const int*                         select = & interlace[iE*nbNodes];
2669       elemNodes.resize( nbNodes );
2670       for ( int iN = 0; iN < nbNodes; ++iN )
2671         elemNodes[iN] = srcNodes[ select[ iN ]];
2672     }
2673     const SMDS_MeshElement* e;
2674     if ( elemQueue )
2675       for ( int iE = 0; iE < nbElems; ++iE )
2676         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2677           elemQueue->push_back( e );
2678   }
2679 }
2680
2681 //=======================================================================
2682 /*
2683  * Split bi-quadratic elements into linear ones without creation of additional nodes
2684  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2685  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2686  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2687  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2688  *   will be split in order to keep the mesh conformal.
2689  *  \param elems - elements to split
2690  */
2691 //=======================================================================
2692
2693 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2694 {
2695   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2696   vector<const SMDS_MeshElement* > splitElems;
2697   list< const SMDS_MeshElement* > elemQueue;
2698   list< const SMDS_MeshElement* >::iterator elemIt;
2699
2700   SMESHDS_Mesh * mesh = GetMeshDS();
2701   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2702   int nbElems, nbNodes;
2703
2704   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2705   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2706   {
2707     elemQueue.clear();
2708     elemQueue.push_back( *elemSetIt );
2709     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2710     {
2711       const SMDS_MeshElement* elem = *elemIt;
2712       switch( elem->GetEntityType() )
2713       {
2714       case SMDSEntity_TriQuad_Hexa: // HEX27
2715       {
2716         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2717         nbElems  = nbNodes = 8;
2718         elemType = & hexaType;
2719
2720         // get nodes for new elements
2721         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2722                                  { 1,9,20,8,    17,22,26,21 },
2723                                  { 2,10,20,9,   18,23,26,22 },
2724                                  { 3,11,20,10,  19,24,26,23 },
2725                                  { 16,21,26,24, 4,12,25,15  },
2726                                  { 17,22,26,21, 5,13,25,12  },
2727                                  { 18,23,26,22, 6,14,25,13  },
2728                                  { 19,24,26,23, 7,15,25,14  }};
2729         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2730
2731         // add boundary faces to elemQueue
2732         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2733                                  { 4,5,6,7, 12,13,14,15, 25 },
2734                                  { 0,1,5,4, 8,17,12,16,  21 },
2735                                  { 1,2,6,5, 9,18,13,17,  22 },
2736                                  { 2,3,7,6, 10,19,14,18, 23 },
2737                                  { 3,0,4,7, 11,16,15,19, 24 }};
2738         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2739
2740         // add boundary segments to elemQueue
2741         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2742                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2743                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2744         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2745         break;
2746       }
2747       case SMDSEntity_BiQuad_Triangle: // TRIA7
2748       {
2749         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2750         nbElems = 3;
2751         nbNodes = 4;
2752         elemType = & quadType;
2753
2754         // get nodes for new elements
2755         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2756         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2757
2758         // add boundary segments to elemQueue
2759         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2760         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2761         break;
2762       }
2763       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2764       {
2765         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2766         nbElems = 4;
2767         nbNodes = 4;
2768         elemType = & quadType;
2769
2770         // get nodes for new elements
2771         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2772         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2773
2774         // add boundary segments to elemQueue
2775         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2776         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2777         break;
2778       }
2779       case SMDSEntity_Quad_Edge:
2780       {
2781         if ( elemIt == elemQueue.begin() )
2782           continue; // an elem is in theElems
2783         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2784         nbElems = 2;
2785         nbNodes = 2;
2786         elemType = & segType;
2787
2788         // get nodes for new elements
2789         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2790         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2791         break;
2792       }
2793       default: continue;
2794       } // switch( elem->GetEntityType() )
2795
2796       // Create new elements
2797
2798       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2799
2800       splitElems.clear();
2801
2802       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2803       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2804       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2805       //elemType->SetID( -1 );
2806
2807       for ( int iE = 0; iE < nbElems; ++iE )
2808         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2809
2810
2811       ReplaceElemInGroups( elem, splitElems, mesh );
2812
2813       if ( subMesh )
2814         for ( size_t i = 0; i < splitElems.size(); ++i )
2815           subMesh->AddElement( splitElems[i] );
2816     }
2817   }
2818 }
2819
2820 //=======================================================================
2821 //function : AddToSameGroups
2822 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2823 //=======================================================================
2824
2825 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2826                                         const SMDS_MeshElement* elemInGroups,
2827                                         SMESHDS_Mesh *          aMesh)
2828 {
2829   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2830   if (!groups.empty()) {
2831     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2832     for ( ; grIt != groups.end(); grIt++ ) {
2833       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2834       if ( group && group->Contains( elemInGroups ))
2835         group->SMDSGroup().Add( elemToAdd );
2836     }
2837   }
2838 }
2839
2840
2841 //=======================================================================
2842 //function : RemoveElemFromGroups
2843 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2844 //=======================================================================
2845 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2846                                              SMESHDS_Mesh *          aMesh)
2847 {
2848   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849   if (!groups.empty())
2850   {
2851     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2852     for (; GrIt != groups.end(); GrIt++)
2853     {
2854       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2855       if (!grp || grp->IsEmpty()) continue;
2856       grp->SMDSGroup().Remove(removeelem);
2857     }
2858   }
2859 }
2860
2861 //================================================================================
2862 /*!
2863  * \brief Replace elemToRm by elemToAdd in the all groups
2864  */
2865 //================================================================================
2866
2867 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2868                                             const SMDS_MeshElement* elemToAdd,
2869                                             SMESHDS_Mesh *          aMesh)
2870 {
2871   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872   if (!groups.empty()) {
2873     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2874     for ( ; grIt != groups.end(); grIt++ ) {
2875       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2876       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2877         group->SMDSGroup().Add( elemToAdd );
2878     }
2879   }
2880 }
2881
2882 //================================================================================
2883 /*!
2884  * \brief Replace elemToRm by elemToAdd in the all groups
2885  */
2886 //================================================================================
2887
2888 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2889                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2890                                             SMESHDS_Mesh *                         aMesh)
2891 {
2892   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2893   if (!groups.empty())
2894   {
2895     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896     for ( ; grIt != groups.end(); grIt++ ) {
2897       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2899         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2900           group->SMDSGroup().Add( elemToAdd[ i ] );
2901     }
2902   }
2903 }
2904
2905 //=======================================================================
2906 //function : QuadToTri
2907 //purpose  : Cut quadrangles into triangles.
2908 //           theCrit is used to select a diagonal to cut
2909 //=======================================================================
2910
2911 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2912                                   const bool         the13Diag)
2913 {
2914   ClearLastCreated();
2915   myLastCreatedElems.reserve( theElems.size() * 2 );
2916
2917   SMESHDS_Mesh *       aMesh = GetMeshDS();
2918   Handle(Geom_Surface) surface;
2919   SMESH_MesherHelper   helper( *GetMesh() );
2920
2921   TIDSortedElemSet::iterator itElem;
2922   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2923   {
2924     const SMDS_MeshElement* elem = *itElem;
2925     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2926       continue;
2927
2928     if ( elem->NbNodes() == 4 ) {
2929       // retrieve element nodes
2930       const SMDS_MeshNode* aNodes [4];
2931       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2932       int i = 0;
2933       while ( itN->more() )
2934         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2935
2936       int aShapeId = FindShape( elem );
2937       const SMDS_MeshElement* newElem1 = 0;
2938       const SMDS_MeshElement* newElem2 = 0;
2939       if ( the13Diag ) {
2940         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2941         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2942       }
2943       else {
2944         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2945         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2946       }
2947       myLastCreatedElems.push_back(newElem1);
2948       myLastCreatedElems.push_back(newElem2);
2949       // put a new triangle on the same shape and add to the same groups
2950       if ( aShapeId )
2951       {
2952         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2953         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2954       }
2955       AddToSameGroups( newElem1, elem, aMesh );
2956       AddToSameGroups( newElem2, elem, aMesh );
2957       aMesh->RemoveElement( elem );
2958     }
2959
2960     // Quadratic quadrangle
2961
2962     else if ( elem->NbNodes() >= 8 )
2963     {
2964       // get surface elem is on
2965       int aShapeId = FindShape( elem );
2966       if ( aShapeId != helper.GetSubShapeID() ) {
2967         surface.Nullify();
2968         TopoDS_Shape shape;
2969         if ( aShapeId > 0 )
2970           shape = aMesh->IndexToShape( aShapeId );
2971         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2972           TopoDS_Face face = TopoDS::Face( shape );
2973           surface = BRep_Tool::Surface( face );
2974           if ( !surface.IsNull() )
2975             helper.SetSubShape( shape );
2976         }
2977       }
2978
2979       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2980       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2981       for ( int i = 0; itN->more(); ++i )
2982         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2983
2984       const SMDS_MeshNode* centrNode = aNodes[8];
2985       if ( centrNode == 0 )
2986       {
2987         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2988                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2989                                            surface.IsNull() );
2990         myLastCreatedNodes.push_back(centrNode);
2991       }
2992
2993       // create a new element
2994       const SMDS_MeshElement* newElem1 = 0;
2995       const SMDS_MeshElement* newElem2 = 0;
2996       if ( the13Diag ) {
2997         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2998                                   aNodes[6], aNodes[7], centrNode );
2999         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3000                                   centrNode, aNodes[4], aNodes[5] );
3001       }
3002       else {
3003         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3004                                   aNodes[7], aNodes[4], centrNode );
3005         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3006                                   centrNode, aNodes[5], aNodes[6] );
3007       }
3008       myLastCreatedElems.push_back(newElem1);
3009       myLastCreatedElems.push_back(newElem2);
3010       // put a new triangle on the same shape and add to the same groups
3011       if ( aShapeId )
3012       {
3013         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3014         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3015       }
3016       AddToSameGroups( newElem1, elem, aMesh );
3017       AddToSameGroups( newElem2, elem, aMesh );
3018       aMesh->RemoveElement( elem );
3019     }
3020   }
3021
3022   return true;
3023 }
3024
3025 //=======================================================================
3026 //function : getAngle
3027 //purpose  :
3028 //=======================================================================
3029
3030 double getAngle(const SMDS_MeshElement * tr1,
3031                 const SMDS_MeshElement * tr2,
3032                 const SMDS_MeshNode *    n1,
3033                 const SMDS_MeshNode *    n2)
3034 {
3035   double angle = 2. * M_PI; // bad angle
3036
3037   // get normals
3038   SMESH::Controls::TSequenceOfXYZ P1, P2;
3039   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3040        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3041     return angle;
3042   gp_Vec N1,N2;
3043   if(!tr1->IsQuadratic())
3044     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3045   else
3046     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3047   if ( N1.SquareMagnitude() <= gp::Resolution() )
3048     return angle;
3049   if(!tr2->IsQuadratic())
3050     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3051   else
3052     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3053   if ( N2.SquareMagnitude() <= gp::Resolution() )
3054     return angle;
3055
3056   // find the first diagonal node n1 in the triangles:
3057   // take in account a diagonal link orientation
3058   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3059   for ( int t = 0; t < 2; t++ ) {
3060     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3061     int i = 0, iDiag = -1;
3062     while ( it->more()) {
3063       const SMDS_MeshElement *n = it->next();
3064       if ( n == n1 || n == n2 ) {
3065         if ( iDiag < 0)
3066           iDiag = i;
3067         else {
3068           if ( i - iDiag == 1 )
3069             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3070           else
3071             nFirst[ t ] = n;
3072           break;
3073         }
3074       }
3075       i++;
3076     }
3077   }
3078   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3079     N2.Reverse();
3080
3081   angle = N1.Angle( N2 );
3082   //SCRUTE( angle );
3083   return angle;
3084 }
3085
3086 // =================================================
3087 // class generating a unique ID for a pair of nodes
3088 // and able to return nodes by that ID
3089 // =================================================
3090 class LinkID_Gen {
3091 public:
3092
3093   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3094     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3095   {}
3096
3097   long GetLinkID (const SMDS_MeshNode * n1,
3098                   const SMDS_MeshNode * n2) const
3099   {
3100     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3101   }
3102
3103   bool GetNodes (const long             theLinkID,
3104                  const SMDS_MeshNode* & theNode1,
3105                  const SMDS_MeshNode* & theNode2) const
3106   {
3107     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3108     if ( !theNode1 ) return false;
3109     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3110     if ( !theNode2 ) return false;
3111     return true;
3112   }
3113
3114 private:
3115   LinkID_Gen();
3116   const SMESHDS_Mesh* myMesh;
3117   long                myMaxID;
3118 };
3119
3120
3121 //=======================================================================
3122 //function : TriToQuad
3123 //purpose  : Fuse neighbour triangles into quadrangles.
3124 //           theCrit is used to select a neighbour to fuse with.
3125 //           theMaxAngle is a max angle between element normals at which
3126 //           fusion is still performed.
3127 //=======================================================================
3128
3129 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3130                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3131                                   const double                         theMaxAngle)
3132 {
3133   ClearLastCreated();
3134   myLastCreatedElems.reserve( theElems.size() / 2 );
3135
3136   if ( !theCrit.get() )
3137     return false;
3138
3139   SMESHDS_Mesh * aMesh = GetMeshDS();
3140
3141   // Prepare data for algo: build
3142   // 1. map of elements with their linkIDs
3143   // 2. map of linkIDs with their elements
3144
3145   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3146   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3147   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3148   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3149
3150   TIDSortedElemSet::iterator itElem;
3151   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3152   {
3153     const SMDS_MeshElement* elem = *itElem;
3154     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3155     bool IsTria = ( elem->NbCornerNodes()==3 );
3156     if (!IsTria) continue;
3157
3158     // retrieve element nodes
3159     const SMDS_MeshNode* aNodes [4];
3160     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3161     int i = 0;
3162     while ( i < 3 )
3163       aNodes[ i++ ] = itN->next();
3164     aNodes[ 3 ] = aNodes[ 0 ];
3165
3166     // fill maps
3167     for ( i = 0; i < 3; i++ ) {
3168       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3169       // check if elements sharing a link can be fused
3170       itLE = mapLi_listEl.find( link );
3171       if ( itLE != mapLi_listEl.end() ) {
3172         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3173           continue;
3174         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3175         //if ( FindShape( elem ) != FindShape( elem2 ))
3176         //  continue; // do not fuse triangles laying on different shapes
3177         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3178           continue; // avoid making badly shaped quads
3179         (*itLE).second.push_back( elem );
3180       }
3181       else {
3182         mapLi_listEl[ link ].push_back( elem );
3183       }
3184       mapEl_setLi [ elem ].insert( link );
3185     }
3186   }
3187   // Clean the maps from the links shared by a sole element, ie
3188   // links to which only one element is bound in mapLi_listEl
3189
3190   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3191     int nbElems = (*itLE).second.size();
3192     if ( nbElems < 2  ) {
3193       const SMDS_MeshElement* elem = (*itLE).second.front();
3194       SMESH_TLink link = (*itLE).first;
3195       mapEl_setLi[ elem ].erase( link );
3196       if ( mapEl_setLi[ elem ].empty() )
3197         mapEl_setLi.erase( elem );
3198     }
3199   }
3200
3201   // Algo: fuse triangles into quadrangles
3202
3203   while ( ! mapEl_setLi.empty() ) {
3204     // Look for the start element:
3205     // the element having the least nb of shared links
3206     const SMDS_MeshElement* startElem = 0;
3207     int minNbLinks = 4;
3208     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3209       int nbLinks = (*itEL).second.size();
3210       if ( nbLinks < minNbLinks ) {
3211         startElem = (*itEL).first;
3212         minNbLinks = nbLinks;
3213         if ( minNbLinks == 1 )
3214           break;
3215       }
3216     }
3217
3218     // search elements to fuse starting from startElem or links of elements
3219     // fused earlyer - startLinks
3220     list< SMESH_TLink > startLinks;
3221     while ( startElem || !startLinks.empty() ) {
3222       while ( !startElem && !startLinks.empty() ) {
3223         // Get an element to start, by a link
3224         SMESH_TLink linkId = startLinks.front();
3225         startLinks.pop_front();
3226         itLE = mapLi_listEl.find( linkId );
3227         if ( itLE != mapLi_listEl.end() ) {
3228           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3229           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3230           for ( ; itE != listElem.end() ; itE++ )
3231             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3232               startElem = (*itE);
3233           mapLi_listEl.erase( itLE );
3234         }
3235       }
3236
3237       if ( startElem ) {
3238         // Get candidates to be fused
3239         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3240         const SMESH_TLink *link12 = 0, *link13 = 0;
3241         startElem = 0;
3242         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3243         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3244         ASSERT( !setLi.empty() );
3245         set< SMESH_TLink >::iterator itLi;
3246         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3247         {
3248           const SMESH_TLink & link = (*itLi);
3249           itLE = mapLi_listEl.find( link );
3250           if ( itLE == mapLi_listEl.end() )
3251             continue;
3252
3253           const SMDS_MeshElement* elem = (*itLE).second.front();
3254           if ( elem == tr1 )
3255             elem = (*itLE).second.back();
3256           mapLi_listEl.erase( itLE );
3257           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3258             continue;
3259           if ( tr2 ) {
3260             tr3 = elem;
3261             link13 = &link;
3262           }
3263           else {
3264             tr2 = elem;
3265             link12 = &link;
3266           }
3267
3268           // add other links of elem to list of links to re-start from
3269           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3270           set< SMESH_TLink >::iterator it;
3271           for ( it = links.begin(); it != links.end(); it++ ) {
3272             const SMESH_TLink& link2 = (*it);
3273             if ( link2 != link )
3274               startLinks.push_back( link2 );
3275           }
3276         }
3277
3278         // Get nodes of possible quadrangles
3279         const SMDS_MeshNode *n12 [4], *n13 [4];
3280         bool Ok12 = false, Ok13 = false;
3281         const SMDS_MeshNode *linkNode1, *linkNode2;
3282         if(tr2) {
3283           linkNode1 = link12->first;
3284           linkNode2 = link12->second;
3285           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3286             Ok12 = true;
3287         }
3288         if(tr3) {
3289           linkNode1 = link13->first;
3290           linkNode2 = link13->second;
3291           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3292             Ok13 = true;
3293         }
3294
3295         // Choose a pair to fuse
3296         if ( Ok12 && Ok13 ) {
3297           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3298           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3299           double aBadRate12 = getBadRate( &quad12, theCrit );
3300           double aBadRate13 = getBadRate( &quad13, theCrit );
3301           if (  aBadRate13 < aBadRate12 )
3302             Ok12 = false;
3303           else
3304             Ok13 = false;
3305         }
3306
3307         // Make quadrangles
3308         // and remove fused elems and remove links from the maps
3309         mapEl_setLi.erase( tr1 );
3310         if ( Ok12 )
3311         {
3312           mapEl_setLi.erase( tr2 );
3313           mapLi_listEl.erase( *link12 );
3314           if ( tr1->NbNodes() == 3 )
3315           {
3316             const SMDS_MeshElement* newElem = 0;
3317             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3318             myLastCreatedElems.push_back(newElem);
3319             AddToSameGroups( newElem, tr1, aMesh );
3320             int aShapeId = tr1->getshapeId();
3321             if ( aShapeId )
3322               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3323             aMesh->RemoveElement( tr1 );
3324             aMesh->RemoveElement( tr2 );
3325           }
3326           else {
3327             vector< const SMDS_MeshNode* > N1;
3328             vector< const SMDS_MeshNode* > N2;
3329             getNodesFromTwoTria(tr1,tr2,N1,N2);
3330             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3331             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3332             // i.e. first nodes from both arrays form a new diagonal
3333             const SMDS_MeshNode* aNodes[8];
3334             aNodes[0] = N1[0];
3335             aNodes[1] = N1[1];
3336             aNodes[2] = N2[0];
3337             aNodes[3] = N2[1];
3338             aNodes[4] = N1[3];
3339             aNodes[5] = N2[5];
3340             aNodes[6] = N2[3];
3341             aNodes[7] = N1[5];
3342             const SMDS_MeshElement* newElem = 0;
3343             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3344               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3345                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3346             else
3347               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3348                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3349             myLastCreatedElems.push_back(newElem);
3350             AddToSameGroups( newElem, tr1, aMesh );
3351             int aShapeId = tr1->getshapeId();
3352             if ( aShapeId )
3353               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3354             aMesh->RemoveElement( tr1 );
3355             aMesh->RemoveElement( tr2 );
3356             // remove middle node (9)
3357             if ( N1[4]->NbInverseElements() == 0 )
3358               aMesh->RemoveNode( N1[4] );
3359             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3360               aMesh->RemoveNode( N1[6] );
3361             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N2[6] );
3363           }
3364         }
3365         else if ( Ok13 )
3366         {
3367           mapEl_setLi.erase( tr3 );
3368           mapLi_listEl.erase( *link13 );
3369           if ( tr1->NbNodes() == 3 ) {
3370             const SMDS_MeshElement* newElem = 0;
3371             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3372             myLastCreatedElems.push_back(newElem);
3373             AddToSameGroups( newElem, tr1, aMesh );
3374             int aShapeId = tr1->getshapeId();
3375             if ( aShapeId )
3376               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377             aMesh->RemoveElement( tr1 );
3378             aMesh->RemoveElement( tr3 );
3379           }
3380           else {
3381             vector< const SMDS_MeshNode* > N1;
3382             vector< const SMDS_MeshNode* > N2;
3383             getNodesFromTwoTria(tr1,tr3,N1,N2);
3384             // now we receive following N1 and N2 (using numeration as above image)
3385             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3386             // i.e. first nodes from both arrays form a new diagonal
3387             const SMDS_MeshNode* aNodes[8];
3388             aNodes[0] = N1[0];
3389             aNodes[1] = N1[1];
3390             aNodes[2] = N2[0];
3391             aNodes[3] = N2[1];
3392             aNodes[4] = N1[3];
3393             aNodes[5] = N2[5];
3394             aNodes[6] = N2[3];
3395             aNodes[7] = N1[5];
3396             const SMDS_MeshElement* newElem = 0;
3397             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3398               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3399                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3400             else
3401               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3402                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3403             myLastCreatedElems.push_back(newElem);
3404             AddToSameGroups( newElem, tr1, aMesh );
3405             int aShapeId = tr1->getshapeId();
3406             if ( aShapeId )
3407               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3408             aMesh->RemoveElement( tr1 );
3409             aMesh->RemoveElement( tr3 );
3410             // remove middle node (9)
3411             if ( N1[4]->NbInverseElements() == 0 )
3412               aMesh->RemoveNode( N1[4] );
3413             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3414               aMesh->RemoveNode( N1[6] );
3415             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N2[6] );
3417           }
3418         }
3419
3420         // Next element to fuse: the rejected one
3421         if ( tr3 )
3422           startElem = Ok12 ? tr3 : tr2;
3423
3424       } // if ( startElem )
3425     } // while ( startElem || !startLinks.empty() )
3426   } // while ( ! mapEl_setLi.empty() )
3427
3428   return true;
3429 }
3430
3431 //================================================================================
3432 /*!
3433  * \brief Return nodes linked to the given one
3434  * \param theNode - the node
3435  * \param linkedNodes - the found nodes
3436  * \param type - the type of elements to check
3437  *
3438  * Medium nodes are ignored
3439  */
3440 //================================================================================
3441
3442 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3443                                        TIDSortedElemSet &   linkedNodes,
3444                                        SMDSAbs_ElementType  type )
3445 {
3446   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3447   while ( elemIt->more() )
3448   {
3449     const SMDS_MeshElement* elem = elemIt->next();
3450     if(elem->GetType() == SMDSAbs_0DElement)
3451       continue;
3452
3453     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3454     if ( elem->GetType() == SMDSAbs_Volume )
3455     {
3456       SMDS_VolumeTool vol( elem );
3457       while ( nodeIt->more() ) {
3458         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3459         if ( theNode != n && vol.IsLinked( theNode, n ))
3460           linkedNodes.insert( n );
3461       }
3462     }
3463     else
3464     {
3465       for ( int i = 0; nodeIt->more(); ++i ) {
3466         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3467         if ( n == theNode ) {
3468           int iBefore = i - 1;
3469           int iAfter  = i + 1;
3470           if ( elem->IsQuadratic() ) {
3471             int nb = elem->NbNodes() / 2;
3472             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3473             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3474           }
3475           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3476           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3477         }
3478       }
3479     }
3480   }
3481 }
3482
3483 //=======================================================================
3484 //function : laplacianSmooth
3485 //purpose  : pulls theNode toward the center of surrounding nodes directly
3486 //           connected to that node along an element edge
3487 //=======================================================================
3488
3489 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3490                      const Handle(Geom_Surface)&          theSurface,
3491                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3492 {
3493   // find surrounding nodes
3494
3495   TIDSortedElemSet nodeSet;
3496   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3497
3498   // compute new coodrs
3499
3500   double coord[] = { 0., 0., 0. };
3501   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3502   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3503     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3504     if ( theSurface.IsNull() ) { // smooth in 3D
3505       coord[0] += node->X();
3506       coord[1] += node->Y();
3507       coord[2] += node->Z();
3508     }
3509     else { // smooth in 2D
3510       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3511       gp_XY* uv = theUVMap[ node ];
3512       coord[0] += uv->X();
3513       coord[1] += uv->Y();
3514     }
3515   }
3516   int nbNodes = nodeSet.size();
3517   if ( !nbNodes )
3518     return;
3519   coord[0] /= nbNodes;
3520   coord[1] /= nbNodes;
3521
3522   if ( !theSurface.IsNull() ) {
3523     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3524     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3525     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3526     coord[0] = p3d.X();
3527     coord[1] = p3d.Y();
3528     coord[2] = p3d.Z();
3529   }
3530   else
3531     coord[2] /= nbNodes;
3532
3533   // move node
3534
3535   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3536 }
3537
3538 //=======================================================================
3539 //function : centroidalSmooth
3540 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3541 //           surrounding elements
3542 //=======================================================================
3543
3544 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3545                       const Handle(Geom_Surface)&          theSurface,
3546                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3547 {
3548   gp_XYZ aNewXYZ(0.,0.,0.);
3549   SMESH::Controls::Area anAreaFunc;
3550   double totalArea = 0.;
3551   int nbElems = 0;
3552
3553   // compute new XYZ
3554
3555   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3556   while ( elemIt->more() )
3557   {
3558     const SMDS_MeshElement* elem = elemIt->next();
3559     nbElems++;
3560
3561     gp_XYZ elemCenter(0.,0.,0.);
3562     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3563     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3564     int nn = elem->NbNodes();
3565     if(elem->IsQuadratic()) nn = nn/2;
3566     int i=0;
3567     //while ( itN->more() ) {
3568     while ( i<nn ) {
3569       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3570       i++;
3571       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3572       aNodePoints.push_back( aP );
3573       if ( !theSurface.IsNull() ) { // smooth in 2D
3574         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3575         gp_XY* uv = theUVMap[ aNode ];
3576         aP.SetCoord( uv->X(), uv->Y(), 0. );
3577       }
3578       elemCenter += aP;
3579     }
3580     double elemArea = anAreaFunc.GetValue( aNodePoints );
3581     totalArea += elemArea;
3582     elemCenter /= nn;
3583     aNewXYZ += elemCenter * elemArea;
3584   }
3585   aNewXYZ /= totalArea;
3586   if ( !theSurface.IsNull() ) {
3587     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3588     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3589   }
3590
3591   // move node
3592
3593   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3594 }
3595
3596 //=======================================================================
3597 //function : getClosestUV
3598 //purpose  : return UV of closest projection
3599 //=======================================================================
3600
3601 static bool getClosestUV (Extrema_GenExtPS& projector,
3602                           const gp_Pnt&     point,
3603                           gp_XY &           result)
3604 {
3605   projector.Perform( point );
3606   if ( projector.IsDone() ) {
3607     double u, v, minVal = DBL_MAX;
3608     for ( int i = projector.NbExt(); i > 0; i-- )
3609       if ( projector.SquareDistance( i ) < minVal ) {
3610         minVal = projector.SquareDistance( i );
3611         projector.Point( i ).Parameter( u, v );
3612       }
3613     result.SetCoord( u, v );
3614     return true;
3615   }
3616   return false;
3617 }
3618
3619 //=======================================================================
3620 //function : Smooth
3621 //purpose  : Smooth theElements during theNbIterations or until a worst
3622 //           element has aspect ratio <= theTgtAspectRatio.
3623 //           Aspect Ratio varies in range [1.0, inf].
3624 //           If theElements is empty, the whole mesh is smoothed.
3625 //           theFixedNodes contains additionally fixed nodes. Nodes built
3626 //           on edges and boundary nodes are always fixed.
3627 //=======================================================================
3628
3629 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3630                                set<const SMDS_MeshNode*> & theFixedNodes,
3631                                const SmoothMethod          theSmoothMethod,
3632                                const int                   theNbIterations,
3633                                double                      theTgtAspectRatio,
3634                                const bool                  the2D)
3635 {
3636   ClearLastCreated();
3637
3638   if ( theTgtAspectRatio < 1.0 )
3639     theTgtAspectRatio = 1.0;
3640
3641   const double disttol = 1.e-16;
3642
3643   SMESH::Controls::AspectRatio aQualityFunc;
3644
3645   SMESHDS_Mesh* aMesh = GetMeshDS();
3646
3647   if ( theElems.empty() ) {
3648     // add all faces to theElems
3649     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3650     while ( fIt->more() ) {
3651       const SMDS_MeshElement* face = fIt->next();
3652       theElems.insert( theElems.end(), face );
3653     }
3654   }
3655   // get all face ids theElems are on
3656   set< int > faceIdSet;
3657   TIDSortedElemSet::iterator itElem;
3658   if ( the2D )
3659     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3660       int fId = FindShape( *itElem );
3661       // check that corresponding submesh exists and a shape is face
3662       if (fId &&
3663           faceIdSet.find( fId ) == faceIdSet.end() &&
3664           aMesh->MeshElements( fId )) {
3665         TopoDS_Shape F = aMesh->IndexToShape( fId );
3666         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3667           faceIdSet.insert( fId );
3668       }
3669     }
3670   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3671
3672   // ===============================================
3673   // smooth elements on each TopoDS_Face separately
3674   // ===============================================
3675
3676   SMESH_MesherHelper helper( *GetMesh() );
3677
3678   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3679   for ( ; fId != faceIdSet.rend(); ++fId )
3680   {
3681     // get face surface and submesh
3682     Handle(Geom_Surface) surface;
3683     SMESHDS_SubMesh* faceSubMesh = 0;
3684     TopoDS_Face face;
3685     double fToler2 = 0;
3686     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3687     bool isUPeriodic = false, isVPeriodic = false;
3688     if ( *fId )
3689     {
3690       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3691       surface = BRep_Tool::Surface( face );
3692       faceSubMesh = aMesh->MeshElements( *fId );
3693       fToler2 = BRep_Tool::Tolerance( face );
3694       fToler2 *= fToler2 * 10.;
3695       isUPeriodic = surface->IsUPeriodic();
3696       // if ( isUPeriodic )
3697       //   surface->UPeriod();
3698       isVPeriodic = surface->IsVPeriodic();
3699       // if ( isVPeriodic )
3700       //   surface->VPeriod();
3701       surface->Bounds( u1, u2, v1, v2 );
3702       helper.SetSubShape( face );
3703     }
3704     // ---------------------------------------------------------
3705     // for elements on a face, find movable and fixed nodes and
3706     // compute UV for them
3707     // ---------------------------------------------------------
3708     bool checkBoundaryNodes = false;
3709     bool isQuadratic = false;
3710     set<const SMDS_MeshNode*> setMovableNodes;
3711     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3712     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3713     list< const SMDS_MeshElement* > elemsOnFace;
3714
3715     Extrema_GenExtPS projector;
3716     GeomAdaptor_Surface surfAdaptor;
3717     if ( !surface.IsNull() ) {
3718       surfAdaptor.Load( surface );
3719       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3720     }
3721     int nbElemOnFace = 0;
3722     itElem = theElems.begin();
3723     // loop on not yet smoothed elements: look for elems on a face
3724     while ( itElem != theElems.end() )
3725     {
3726       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3727         break; // all elements found
3728
3729       const SMDS_MeshElement* elem = *itElem;
3730       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3731            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3732         ++itElem;
3733         continue;
3734       }
3735       elemsOnFace.push_back( elem );
3736       theElems.erase( itElem++ );
3737       nbElemOnFace++;
3738
3739       if ( !isQuadratic )
3740         isQuadratic = elem->IsQuadratic();
3741
3742       // get movable nodes of elem
3743       const SMDS_MeshNode* node;
3744       SMDS_TypeOfPosition posType;
3745       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3746       int nn = 0, nbn =  elem->NbNodes();
3747       if(elem->IsQuadratic())
3748         nbn = nbn/2;
3749       while ( nn++ < nbn ) {
3750         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3751         const SMDS_PositionPtr& pos = node->GetPosition();
3752         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3753         if (posType != SMDS_TOP_EDGE &&
3754             posType != SMDS_TOP_VERTEX &&
3755             theFixedNodes.find( node ) == theFixedNodes.end())
3756         {
3757           // check if all faces around the node are on faceSubMesh
3758           // because a node on edge may be bound to face
3759           bool all = true;
3760           if ( faceSubMesh ) {
3761             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3762             while ( eIt->more() && all ) {
3763               const SMDS_MeshElement* e = eIt->next();
3764               all = faceSubMesh->Contains( e );
3765             }
3766           }
3767           if ( all )
3768             setMovableNodes.insert( node );
3769           else
3770             checkBoundaryNodes = true;
3771         }
3772         if ( posType == SMDS_TOP_3DSPACE )
3773           checkBoundaryNodes = true;
3774       }
3775
3776       if ( surface.IsNull() )
3777         continue;
3778
3779       // get nodes to check UV
3780       list< const SMDS_MeshNode* > uvCheckNodes;
3781       const SMDS_MeshNode* nodeInFace = 0;
3782       itN = elem->nodesIterator();
3783       nn = 0; nbn =  elem->NbNodes();
3784       if(elem->IsQuadratic())
3785         nbn = nbn/2;
3786       while ( nn++ < nbn ) {
3787         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3788         if ( node->GetPosition()->GetDim() == 2 )
3789           nodeInFace = node;
3790         if ( uvMap.find( node ) == uvMap.end() )
3791           uvCheckNodes.push_back( node );
3792         // add nodes of elems sharing node
3793         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3794         //         while ( eIt->more() ) {
3795         //           const SMDS_MeshElement* e = eIt->next();
3796         //           if ( e != elem ) {
3797         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3798         //             while ( nIt->more() ) {
3799         //               const SMDS_MeshNode* n =
3800         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3801         //               if ( uvMap.find( n ) == uvMap.end() )
3802         //                 uvCheckNodes.push_back( n );
3803         //             }
3804         //           }
3805         //         }
3806       }
3807       // check UV on face
3808       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3809       for ( ; n != uvCheckNodes.end(); ++n ) {
3810         node = *n;
3811         gp_XY uv( 0, 0 );
3812         const SMDS_PositionPtr& pos = node->GetPosition();
3813         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3814         // get existing UV
3815         if ( pos )
3816         {
3817           bool toCheck = true;
3818           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3819         }
3820         // compute not existing UV
3821         bool project = ( posType == SMDS_TOP_3DSPACE );
3822         // double dist1 = DBL_MAX, dist2 = 0;
3823         // if ( posType != SMDS_TOP_3DSPACE ) {
3824         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3825         //   project = dist1 > fToler2;
3826         // }
3827         if ( project ) { // compute new UV
3828           gp_XY newUV;
3829           gp_Pnt pNode = SMESH_NodeXYZ( node );
3830           if ( !getClosestUV( projector, pNode, newUV )) {
3831             MESSAGE("Node Projection Failed " << node);
3832           }
3833           else {
3834             if ( isUPeriodic )
3835               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3836             if ( isVPeriodic )
3837               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3838             // check new UV
3839             // if ( posType != SMDS_TOP_3DSPACE )
3840             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3841             // if ( dist2 < dist1 )
3842             uv = newUV;
3843           }
3844         }
3845         // store UV in the map
3846         listUV.push_back( uv );
3847         uvMap.insert( make_pair( node, &listUV.back() ));
3848       }
3849     } // loop on not yet smoothed elements
3850
3851     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3852       checkBoundaryNodes = true;
3853
3854     // fix nodes on mesh boundary
3855
3856     if ( checkBoundaryNodes ) {
3857       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3858       map< SMESH_TLink, int >::iterator link_nb;
3859       // put all elements links to linkNbMap
3860       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3861       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3862         const SMDS_MeshElement* elem = (*elemIt);
3863         int nbn =  elem->NbCornerNodes();
3864         // loop on elem links: insert them in linkNbMap
3865         for ( int iN = 0; iN < nbn; ++iN ) {
3866           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3867           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3868           SMESH_TLink link( n1, n2 );
3869           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3870           link_nb->second++;
3871         }
3872       }
3873       // remove nodes that are in links encountered only once from setMovableNodes
3874       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3875         if ( link_nb->second == 1 ) {
3876           setMovableNodes.erase( link_nb->first.node1() );
3877           setMovableNodes.erase( link_nb->first.node2() );
3878         }
3879       }
3880     }
3881
3882     // -----------------------------------------------------
3883     // for nodes on seam edge, compute one more UV ( uvMap2 );
3884     // find movable nodes linked to nodes on seam and which
3885     // are to be smoothed using the second UV ( uvMap2 )
3886     // -----------------------------------------------------
3887
3888     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3889     if ( !surface.IsNull() ) {
3890       TopExp_Explorer eExp( face, TopAbs_EDGE );
3891       for ( ; eExp.More(); eExp.Next() ) {
3892         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3893         if ( !BRep_Tool::IsClosed( edge, face ))
3894           continue;
3895         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3896         if ( !sm ) continue;
3897         // find out which parameter varies for a node on seam
3898         double f,l;
3899         gp_Pnt2d uv1, uv2;
3900         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3901         if ( pcurve.IsNull() ) continue;
3902         uv1 = pcurve->Value( f );
3903         edge.Reverse();
3904         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3905         if ( pcurve.IsNull() ) continue;
3906         uv2 = pcurve->Value( f );
3907         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3908         // assure uv1 < uv2
3909         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3910           std::swap( uv1, uv2 );
3911         // get nodes on seam and its vertices
3912         list< const SMDS_MeshNode* > seamNodes;
3913         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3914         while ( nSeamIt->more() ) {
3915           const SMDS_MeshNode* node = nSeamIt->next();
3916           if ( !isQuadratic || !IsMedium( node ))
3917             seamNodes.push_back( node );
3918         }
3919         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3920         for ( ; vExp.More(); vExp.Next() ) {
3921           sm = aMesh->MeshElements( vExp.Current() );
3922           if ( sm ) {
3923             nSeamIt = sm->GetNodes();
3924             while ( nSeamIt->more() )
3925               seamNodes.push_back( nSeamIt->next() );
3926           }
3927         }
3928         // loop on nodes on seam
3929         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3930         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3931           const SMDS_MeshNode* nSeam = *noSeIt;
3932           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3933           if ( n_uv == uvMap.end() )
3934             continue;
3935           // set the first UV
3936           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3937           // set the second UV
3938           listUV.push_back( *n_uv->second );
3939           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3940           if ( uvMap2.empty() )
3941             uvMap2 = uvMap; // copy the uvMap contents
3942           uvMap2[ nSeam ] = &listUV.back();
3943
3944           // collect movable nodes linked to ones on seam in nodesNearSeam
3945           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3946           while ( eIt->more() ) {
3947             const SMDS_MeshElement* e = eIt->next();
3948             int nbUseMap1 = 0, nbUseMap2 = 0;
3949             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3950             int nn = 0, nbn =  e->NbNodes();
3951             if(e->IsQuadratic()) nbn = nbn/2;
3952             while ( nn++ < nbn )
3953             {
3954               const SMDS_MeshNode* n =
3955                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3956               if (n == nSeam ||
3957                   setMovableNodes.find( n ) == setMovableNodes.end() )
3958                 continue;
3959               // add only nodes being closer to uv2 than to uv1
3960               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3961               //              0.5 * ( n->Y() + nSeam->Y() ),
3962               //              0.5 * ( n->Z() + nSeam->Z() ));
3963               // gp_XY uv;
3964               // getClosestUV( projector, pMid, uv );
3965               double x = uvMap[ n ]->Coord( iPar );
3966               if ( Abs( uv1.Coord( iPar ) - x ) >
3967                    Abs( uv2.Coord( iPar ) - x )) {
3968                 nodesNearSeam.insert( n );
3969                 nbUseMap2++;
3970               }
3971               else
3972                 nbUseMap1++;
3973             }
3974             // for centroidalSmooth all element nodes must
3975             // be on one side of a seam
3976             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3977               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3978               nn = 0;
3979               while ( nn++ < nbn ) {
3980                 const SMDS_MeshNode* n =
3981                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3982                 setMovableNodes.erase( n );
3983               }
3984             }
3985           }
3986         } // loop on nodes on seam
3987       } // loop on edge of a face
3988     } // if ( !face.IsNull() )
3989
3990     if ( setMovableNodes.empty() ) {
3991       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3992       continue; // goto next face
3993     }
3994
3995     // -------------
3996     // SMOOTHING //
3997     // -------------
3998
3999     int it = -1;
4000     double maxRatio = -1., maxDisplacement = -1.;
4001     set<const SMDS_MeshNode*>::iterator nodeToMove;
4002     for ( it = 0; it < theNbIterations; it++ ) {
4003       maxDisplacement = 0.;
4004       nodeToMove = setMovableNodes.begin();
4005       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4006         const SMDS_MeshNode* node = (*nodeToMove);
4007         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4008
4009         // smooth
4010         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4011         if ( theSmoothMethod == LAPLACIAN )
4012           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4013         else
4014           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015
4016         // node displacement
4017         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4018         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4019         if ( aDispl > maxDisplacement )
4020           maxDisplacement = aDispl;
4021       }
4022       // no node movement => exit
4023       //if ( maxDisplacement < 1.e-16 ) {
4024       if ( maxDisplacement < disttol ) {
4025         MESSAGE("-- no node movement --");
4026         break;
4027       }
4028
4029       // check elements quality
4030       maxRatio  = 0;
4031       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4032       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4033         const SMDS_MeshElement* elem = (*elemIt);
4034         if ( !elem || elem->GetType() != SMDSAbs_Face )
4035           continue;
4036         SMESH::Controls::TSequenceOfXYZ aPoints;
4037         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4038           double aValue = aQualityFunc.GetValue( aPoints );
4039           if ( aValue > maxRatio )
4040             maxRatio = aValue;
4041         }
4042       }
4043       if ( maxRatio <= theTgtAspectRatio ) {
4044         //MESSAGE("-- quality achieved --");
4045         break;
4046       }
4047       if (it+1 == theNbIterations) {
4048         //MESSAGE("-- Iteration limit exceeded --");
4049       }
4050     } // smoothing iterations
4051
4052     // MESSAGE(" Face id: " << *fId <<
4053     //         " Nb iterstions: " << it <<
4054     //         " Displacement: " << maxDisplacement <<
4055     //         " Aspect Ratio " << maxRatio);
4056
4057     // ---------------------------------------
4058     // new nodes positions are computed,
4059     // record movement in DS and set new UV
4060     // ---------------------------------------
4061     nodeToMove = setMovableNodes.begin();
4062     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4063       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4064       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4065       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4066       if ( node_uv != uvMap.end() ) {
4067         gp_XY* uv = node_uv->second;
4068         node->SetPosition
4069           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4070       }
4071     }
4072
4073     // move medium nodes of quadratic elements
4074     if ( isQuadratic )
4075     {
4076       vector<const SMDS_MeshNode*> nodes;
4077       bool checkUV;
4078       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4079       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4080       {
4081         const SMDS_MeshElement* QF = *elemIt;
4082         if ( QF->IsQuadratic() )
4083         {
4084           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4085                         SMDS_MeshElement::iterator() );
4086           nodes.push_back( nodes[0] );
4087           gp_Pnt xyz;
4088           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4089           {
4090             if ( !surface.IsNull() )
4091             {
4092               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4093               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4094               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4095               xyz = surface->Value( uv.X(), uv.Y() );
4096             }
4097             else {
4098               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4099             }
4100             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4101               // we have to move a medium node
4102               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4103           }
4104         }
4105       }
4106     }
4107
4108   } // loop on face ids
4109
4110 }
4111
4112 namespace
4113 {
4114   //=======================================================================
4115   //function : isReverse
4116   //purpose  : Return true if normal of prevNodes is not co-directied with
4117   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4118   //           iNotSame is where prevNodes and nextNodes are different.
4119   //           If result is true then future volume orientation is OK
4120   //=======================================================================
4121
4122   bool isReverse(const SMDS_MeshElement*             face,
4123                  const vector<const SMDS_MeshNode*>& prevNodes,
4124                  const vector<const SMDS_MeshNode*>& nextNodes,
4125                  const int                           iNotSame)
4126   {
4127
4128     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4129     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4130     gp_XYZ extrDir( pN - pP ), faceNorm;
4131     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4132
4133     return faceNorm * extrDir < 0.0;
4134   }
4135
4136   //================================================================================
4137   /*!
4138    * \brief Assure that theElemSets[0] holds elements, not nodes
4139    */
4140   //================================================================================
4141
4142   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4143   {
4144     if ( !theElemSets[0].empty() &&
4145          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4146     {
4147       std::swap( theElemSets[0], theElemSets[1] );
4148     }
4149     else if ( !theElemSets[1].empty() &&
4150               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4151     {
4152       std::swap( theElemSets[0], theElemSets[1] );
4153     }
4154   }
4155 }
4156
4157 //=======================================================================
4158 /*!
4159  * \brief Create elements by sweeping an element
4160  * \param elem - element to sweep
4161  * \param newNodesItVec - nodes generated from each node of the element
4162  * \param newElems - generated elements
4163  * \param nbSteps - number of sweeping steps
4164  * \param srcElements - to append elem for each generated element
4165  */
4166 //=======================================================================
4167
4168 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4169                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4170                                     list<const SMDS_MeshElement*>&        newElems,
4171                                     const size_t                          nbSteps,
4172                                     SMESH_SequenceOfElemPtr&              srcElements)
4173 {
4174   SMESHDS_Mesh* aMesh = GetMeshDS();
4175
4176   const int           nbNodes = elem->NbNodes();
4177   const int         nbCorners = elem->NbCornerNodes();
4178   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4179                                                           polyhedron creation !!! */
4180   // Loop on elem nodes:
4181   // find new nodes and detect same nodes indices
4182   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4183   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4184   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4185   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4186
4187   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4188   vector<int> sames(nbNodes);
4189   vector<bool> isSingleNode(nbNodes);
4190
4191   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4192     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4193     const SMDS_MeshNode*                         node = nnIt->first;
4194     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4195     if ( listNewNodes.empty() )
4196       return;
4197
4198     itNN   [ iNode ] = listNewNodes.begin();
4199     prevNod[ iNode ] = node;
4200     nextNod[ iNode ] = listNewNodes.front();
4201
4202     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4203                                                              corner node of linear */
4204     if ( prevNod[ iNode ] != nextNod [ iNode ])
4205       nbDouble += !isSingleNode[iNode];
4206
4207     if( iNode < nbCorners ) { // check corners only
4208       if ( prevNod[ iNode ] == nextNod [ iNode ])
4209         sames[nbSame++] = iNode;
4210       else
4211         iNotSameNode = iNode;
4212     }
4213   }
4214
4215   if ( nbSame == nbNodes || nbSame > 2) {
4216     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4217     return;
4218   }
4219
4220   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4221   {
4222     // fix nodes order to have bottom normal external
4223     if ( baseType == SMDSEntity_Polygon )
4224     {
4225       std::reverse( itNN.begin(), itNN.end() );
4226       std::reverse( prevNod.begin(), prevNod.end() );
4227       std::reverse( midlNod.begin(), midlNod.end() );
4228       std::reverse( nextNod.begin(), nextNod.end() );
4229       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4230     }
4231     else
4232     {
4233       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4234       SMDS_MeshCell::applyInterlace( ind, itNN );
4235       SMDS_MeshCell::applyInterlace( ind, prevNod );
4236       SMDS_MeshCell::applyInterlace( ind, nextNod );
4237       SMDS_MeshCell::applyInterlace( ind, midlNod );
4238       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4239       if ( nbSame > 0 )
4240       {
4241         sames[nbSame] = iNotSameNode;
4242         for ( int j = 0; j <= nbSame; ++j )
4243           for ( size_t i = 0; i < ind.size(); ++i )
4244             if ( ind[i] == sames[j] )
4245             {
4246               sames[j] = i;
4247               break;
4248             }
4249         iNotSameNode = sames[nbSame];
4250       }
4251     }
4252   }
4253   else if ( elem->GetType() == SMDSAbs_Edge )
4254   {
4255     // orient a new face same as adjacent one
4256     int i1, i2;
4257     const SMDS_MeshElement* e;
4258     TIDSortedElemSet dummy;
4259     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4260         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4261         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4262     {
4263       // there is an adjacent face, check order of nodes in it
4264       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4265       if ( sameOrder )
4266       {
4267         std::swap( itNN[0],    itNN[1] );
4268         std::swap( prevNod[0], prevNod[1] );
4269         std::swap( nextNod[0], nextNod[1] );
4270         std::swap( isSingleNode[0], isSingleNode[1] );
4271         if ( nbSame > 0 )
4272           sames[0] = 1 - sames[0];
4273         iNotSameNode = 1 - iNotSameNode;
4274       }
4275     }
4276   }
4277
4278   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4279   if ( nbSame > 0 ) {
4280     iSameNode    = sames[ nbSame-1 ];
4281     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4282     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4283     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4284   }
4285
4286   if ( baseType == SMDSEntity_Polygon )
4287   {
4288     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4289     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4290   }
4291   else if ( baseType == SMDSEntity_Quad_Polygon )
4292   {
4293     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4294     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4295   }
4296
4297   // make new elements
4298   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4299   {
4300     // get next nodes
4301     for ( iNode = 0; iNode < nbNodes; iNode++ )
4302     {
4303       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4304       nextNod[ iNode ] = *itNN[ iNode ]++;
4305     }
4306
4307     SMDS_MeshElement* aNewElem = 0;
4308     /*if(!elem->IsPoly())*/ {
4309       switch ( baseType ) {
4310       case SMDSEntity_0D:
4311       case SMDSEntity_Node: { // sweep NODE
4312         if ( nbSame == 0 ) {
4313           if ( isSingleNode[0] )
4314             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4315           else
4316             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4317         }
4318         else
4319           return;
4320         break;
4321       }
4322       case SMDSEntity_Edge: { // sweep EDGE
4323         if ( nbDouble == 0 )
4324         {
4325           if ( nbSame == 0 ) // ---> quadrangle
4326             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4327                                       nextNod[ 1 ], nextNod[ 0 ] );
4328           else               // ---> triangle
4329             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4330                                       nextNod[ iNotSameNode ] );
4331         }
4332         else                 // ---> polygon
4333         {
4334           vector<const SMDS_MeshNode*> poly_nodes;
4335           poly_nodes.push_back( prevNod[0] );
4336           poly_nodes.push_back( prevNod[1] );
4337           if ( prevNod[1] != nextNod[1] )
4338           {
4339             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4340             poly_nodes.push_back( nextNod[1] );
4341           }
4342           if ( prevNod[0] != nextNod[0] )
4343           {
4344             poly_nodes.push_back( nextNod[0] );
4345             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4346           }
4347           switch ( poly_nodes.size() ) {
4348           case 3:
4349             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4350             break;
4351           case 4:
4352             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4353                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4354             break;
4355           default:
4356             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4357           }
4358         }
4359         break;
4360       }
4361       case SMDSEntity_Triangle: // TRIANGLE --->
4362       {
4363         if ( nbDouble > 0 ) break;
4364         if ( nbSame == 0 )       // ---> pentahedron
4365           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4366                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4367
4368         else if ( nbSame == 1 )  // ---> pyramid
4369           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4370                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4371                                        nextNod[ iSameNode ]);
4372
4373         else // 2 same nodes:       ---> tetrahedron
4374           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4375                                        nextNod[ iNotSameNode ]);
4376         break;
4377       }
4378       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4379       {
4380         if ( nbSame == 2 )
4381           return;
4382         if ( nbDouble+nbSame == 2 )
4383         {
4384           if(nbSame==0) {      // ---> quadratic quadrangle
4385             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4387           }
4388           else { //(nbSame==1) // ---> quadratic triangle
4389             if(sames[0]==2) {
4390               return; // medium node on axis
4391             }
4392             else if(sames[0]==0)
4393               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4394                                         prevNod[2], midlNod[1], nextNod[2] );
4395             else // sames[0]==1
4396               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4397                                         prevNod[2], nextNod[2], midlNod[0]);
4398           }
4399         }
4400         else if ( nbDouble == 3 )
4401         {
4402           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4403             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4404                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4405           }
4406         }
4407         else
4408           return;
4409         break;
4410       }
4411       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4412         if ( nbDouble > 0 ) break;
4413
4414         if ( nbSame == 0 )       // ---> hexahedron
4415           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4416                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4417
4418         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4419           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4420                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4421                                        nextNod[ iSameNode ]);
4422           newElems.push_back( aNewElem );
4423           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4424                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4425                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4426         }
4427         else if ( nbSame == 2 ) { // ---> pentahedron
4428           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4429             // iBeforeSame is same too
4430             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4431                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4432                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4433           else
4434             // iAfterSame is same too
4435             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4436                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4437                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4438         }
4439         break;
4440       }
4441       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4442       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4443         if ( nbDouble+nbSame != 3 ) break;
4444         if(nbSame==0) {
4445           // --->  pentahedron with 15 nodes
4446           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4447                                        nextNod[0], nextNod[1], nextNod[2],
4448                                        prevNod[3], prevNod[4], prevNod[5],
4449                                        nextNod[3], nextNod[4], nextNod[5],
4450                                        midlNod[0], midlNod[1], midlNod[2]);
4451         }
4452         else if(nbSame==1) {
4453           // --->  2d order pyramid of 13 nodes
4454           int apex = iSameNode;
4455           int i0 = ( apex + 1 ) % nbCorners;
4456           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4457           int i0a = apex + 3;
4458           int i1a = i1 + 3;
4459           int i01 = i0 + 3;
4460           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4461                                       nextNod[i0], nextNod[i1], prevNod[apex],
4462                                       prevNod[i01], midlNod[i0],
4463                                       nextNod[i01], midlNod[i1],
4464                                       prevNod[i1a], prevNod[i0a],
4465                                       nextNod[i0a], nextNod[i1a]);
4466         }
4467         else if(nbSame==2) {
4468           // --->  2d order tetrahedron of 10 nodes
4469           int n1 = iNotSameNode;
4470           int n2 = ( n1 + 1             ) % nbCorners;
4471           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4472           int n12 = n1 + 3;
4473           int n23 = n2 + 3;
4474           int n31 = n3 + 3;
4475           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4476                                        prevNod[n12], prevNod[n23], prevNod[n31],
4477                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4478         }
4479         break;
4480       }
4481       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4482         if( nbSame == 0 ) {
4483           if ( nbDouble != 4 ) break;
4484           // --->  hexahedron with 20 nodes
4485           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4486                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4487                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4488                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4489                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4490         }
4491         else if(nbSame==1) {
4492           // ---> pyramid + pentahedron - can not be created since it is needed
4493           // additional middle node at the center of face
4494           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4495           return;
4496         }
4497         else if( nbSame == 2 ) {
4498           if ( nbDouble != 2 ) break;
4499           // --->  2d order Pentahedron with 15 nodes
4500           int n1,n2,n4,n5;
4501           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4502             // iBeforeSame is same too
4503             n1 = iBeforeSame;
4504             n2 = iOpposSame;
4505             n4 = iSameNode;
4506             n5 = iAfterSame;
4507           }
4508           else {
4509             // iAfterSame is same too
4510             n1 = iSameNode;
4511             n2 = iBeforeSame;
4512             n4 = iAfterSame;
4513             n5 = iOpposSame;
4514           }
4515           int n12 = n2 + 4;
4516           int n45 = n4 + 4;
4517           int n14 = n1 + 4;
4518           int n25 = n5 + 4;
4519           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4520                                        prevNod[n4], prevNod[n5], nextNod[n5],
4521                                        prevNod[n12], midlNod[n2], nextNod[n12],
4522                                        prevNod[n45], midlNod[n5], nextNod[n45],
4523                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4524         }
4525         break;
4526       }
4527       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4528
4529         if( nbSame == 0 && nbDouble == 9 ) {
4530           // --->  tri-quadratic hexahedron with 27 nodes
4531           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4532                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4533                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4534                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4535                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4536                                        prevNod[8], // bottom center
4537                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4538                                        nextNod[8], // top center
4539                                        midlNod[8]);// elem center
4540         }
4541         else
4542         {
4543           return;
4544         }
4545         break;
4546       }
4547       case SMDSEntity_Polygon: { // sweep POLYGON
4548
4549         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4550           // --->  hexagonal prism
4551           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4552                                        prevNod[3], prevNod[4], prevNod[5],
4553                                        nextNod[0], nextNod[1], nextNod[2],
4554                                        nextNod[3], nextNod[4], nextNod[5]);
4555         }
4556         break;
4557       }
4558       case SMDSEntity_Ball:
4559         return;
4560
4561       default:
4562         break;
4563       } // switch ( baseType )
4564     } // scope
4565
4566     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4567     {
4568       if ( baseType != SMDSEntity_Polygon )
4569       {
4570         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4571         SMDS_MeshCell::applyInterlace( ind, prevNod );
4572         SMDS_MeshCell::applyInterlace( ind, nextNod );
4573         SMDS_MeshCell::applyInterlace( ind, midlNod );
4574         SMDS_MeshCell::applyInterlace( ind, itNN );
4575         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4576         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4577       }
4578       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4579       vector<int> quantities (nbNodes + 2);
4580       polyedre_nodes.clear();
4581       quantities.clear();
4582
4583       // bottom of prism
4584       for (int inode = 0; inode < nbNodes; inode++)
4585         polyedre_nodes.push_back( prevNod[inode] );
4586       quantities.push_back( nbNodes );
4587
4588       // top of prism
4589       polyedre_nodes.push_back( nextNod[0] );
4590       for (int inode = nbNodes; inode-1; --inode )
4591         polyedre_nodes.push_back( nextNod[inode-1] );
4592       quantities.push_back( nbNodes );
4593
4594       // side faces
4595       // 3--6--2
4596       // |     |
4597       // 7     5
4598       // |     |
4599       // 0--4--1
4600       const int iQuad = elem->IsQuadratic();
4601       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4602       {
4603         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4604         int inextface = (iface+1+iQuad) % nbNodes;
4605         int imid      = (iface+1) % nbNodes;
4606         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4607         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4608         polyedre_nodes.push_back( prevNod[iface] );             // 1
4609         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4610         {
4611           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4612           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4613         }
4614         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4615         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4616         {
4617           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4618           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4619         }
4620         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4621         if ( nbFaceNodes > 2 )
4622           quantities.push_back( nbFaceNodes );
4623         else // degenerated face
4624           polyedre_nodes.resize( prevNbNodes );
4625       }
4626       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4627
4628     } // try to create a polyherdal prism
4629
4630     if ( aNewElem ) {
4631       newElems.push_back( aNewElem );
4632       myLastCreatedElems.push_back(aNewElem);
4633       srcElements.push_back( elem );
4634     }
4635
4636     // set new prev nodes
4637     for ( iNode = 0; iNode < nbNodes; iNode++ )
4638       prevNod[ iNode ] = nextNod[ iNode ];
4639
4640   } // loop on steps
4641 }
4642
4643 //=======================================================================
4644 /*!
4645  * \brief Create 1D and 2D elements around swept elements
4646  * \param mapNewNodes - source nodes and ones generated from them
4647  * \param newElemsMap - source elements and ones generated from them
4648  * \param elemNewNodesMap - nodes generated from each node of each element
4649  * \param elemSet - all swept elements
4650  * \param nbSteps - number of sweeping steps
4651  * \param srcElements - to append elem for each generated element
4652  */
4653 //=======================================================================
4654
4655 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4656                                   TTElemOfElemListMap &    newElemsMap,
4657                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4658                                   TIDSortedElemSet&        elemSet,
4659                                   const int                nbSteps,
4660                                   SMESH_SequenceOfElemPtr& srcElements)
4661 {
4662   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4663   SMESHDS_Mesh* aMesh = GetMeshDS();
4664
4665   // Find nodes belonging to only one initial element - sweep them into edges.
4666
4667   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4668   for ( ; nList != mapNewNodes.end(); nList++ )
4669   {
4670     const SMDS_MeshNode* node =
4671       static_cast<const SMDS_MeshNode*>( nList->first );
4672     if ( newElemsMap.count( node ))
4673       continue; // node was extruded into edge
4674     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4675     int nbInitElems = 0;
4676     const SMDS_MeshElement* el = 0;
4677     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4678     while ( eIt->more() && nbInitElems < 2 ) {
4679       const SMDS_MeshElement* e = eIt->next();
4680       SMDSAbs_ElementType  type = e->GetType();
4681       if ( type == SMDSAbs_Volume ||
4682            type < highType ||
4683            !elemSet.count(e))
4684         continue;
4685       if ( type > highType ) {
4686         nbInitElems = 0;
4687         highType    = type;
4688       }
4689       el = e;
4690       ++nbInitElems;
4691     }
4692     if ( nbInitElems == 1 ) {
4693       bool NotCreateEdge = el && el->IsMediumNode(node);
4694       if(!NotCreateEdge) {
4695         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4696         list<const SMDS_MeshElement*> newEdges;
4697         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4698       }
4699     }
4700   }
4701
4702   // Make a ceiling for each element ie an equal element of last new nodes.
4703   // Find free links of faces - make edges and sweep them into faces.
4704
4705   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4706
4707   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4708   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4709   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4710   {
4711     const SMDS_MeshElement* elem = itElem->first;
4712     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4713
4714     if(itElem->second.size()==0) continue;
4715
4716     const bool isQuadratic = elem->IsQuadratic();
4717
4718     if ( elem->GetType() == SMDSAbs_Edge ) {
4719       // create a ceiling edge
4720       if ( !isQuadratic ) {
4721         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4722                                vecNewNodes[ 1 ]->second.back())) {
4723           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4724                                                       vecNewNodes[ 1 ]->second.back()));
4725           srcElements.push_back( elem );
4726         }
4727       }
4728       else {
4729         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4730                                vecNewNodes[ 1 ]->second.back(),
4731                                vecNewNodes[ 2 ]->second.back())) {
4732           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4733                                                       vecNewNodes[ 1 ]->second.back(),
4734                                                       vecNewNodes[ 2 ]->second.back()));
4735           srcElements.push_back( elem );
4736         }
4737       }
4738     }
4739     if ( elem->GetType() != SMDSAbs_Face )
4740       continue;
4741
4742     bool hasFreeLinks = false;
4743
4744     TIDSortedElemSet avoidSet;
4745     avoidSet.insert( elem );
4746
4747     set<const SMDS_MeshNode*> aFaceLastNodes;
4748     int iNode, nbNodes = vecNewNodes.size();
4749     if ( !isQuadratic ) {
4750       // loop on the face nodes
4751       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4752         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4753         // look for free links of the face
4754         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4755         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4756         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4757         // check if a link n1-n2 is free
4758         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4759           hasFreeLinks = true;
4760           // make a new edge and a ceiling for a new edge
4761           const SMDS_MeshElement* edge;
4762           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4763             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4764             srcElements.push_back( myLastCreatedElems.back() );
4765           }
4766           n1 = vecNewNodes[ iNode ]->second.back();
4767           n2 = vecNewNodes[ iNext ]->second.back();
4768           if ( !aMesh->FindEdge( n1, n2 )) {
4769             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4770             srcElements.push_back( edge );
4771           }
4772         }
4773       }
4774     }
4775     else { // elem is quadratic face
4776       int nbn = nbNodes/2;
4777       for ( iNode = 0; iNode < nbn; iNode++ ) {
4778         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4779         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4780         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4783         // check if a link is free
4784         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4785              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4786              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4787           hasFreeLinks = true;
4788           // make an edge and a ceiling for a new edge
4789           // find medium node
4790           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4791             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4792             srcElements.push_back( elem );
4793           }
4794           n1 = vecNewNodes[ iNode ]->second.back();
4795           n2 = vecNewNodes[ iNext ]->second.back();
4796           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4797           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4798             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4799             srcElements.push_back( elem );
4800           }
4801         }
4802       }
4803       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4804         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4805       }
4806     }
4807
4808     // sweep free links into faces
4809
4810     if ( hasFreeLinks ) {
4811       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4812       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4813
4814       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4815       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4816       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4817         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4818         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4819       }
4820       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4821         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4822         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4823       }
4824       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4825         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4826         std::advance( v, volNb );
4827         // find indices of free faces of a volume and their source edges
4828         list< int > freeInd;
4829         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4830         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4831         int iF, nbF = vTool.NbFaces();
4832         for ( iF = 0; iF < nbF; iF ++ ) {
4833           if ( vTool.IsFreeFace( iF ) &&
4834                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4835                initNodeSet != faceNodeSet) // except an initial face
4836           {
4837             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4838               continue;
4839             if ( faceNodeSet == initNodeSetNoCenter )
4840               continue;
4841             freeInd.push_back( iF );
4842             // find source edge of a free face iF
4843             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4844             vector<const SMDS_MeshNode*>::iterator lastCommom;
4845             commonNodes.resize( nbNodes, 0 );
4846             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4847                                                 initNodeSet.begin(), initNodeSet.end(),
4848                                                 commonNodes.begin());
4849             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4850               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4851             else
4852               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4853 #ifdef _DEBUG_
4854             if ( !srcEdges.back() )
4855             {
4856               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4857                    << iF << " of volume #" << vTool.ID() << endl;
4858             }
4859 #endif
4860           }
4861         }
4862         if ( freeInd.empty() )
4863           continue;
4864
4865         // create wall faces for all steps;
4866         // if such a face has been already created by sweep of edge,
4867         // assure that its orientation is OK
4868         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4869         {
4870           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4871           vTool.SetExternalNormal();
4872           const int nextShift = vTool.IsForward() ? +1 : -1;
4873           list< int >::iterator ind = freeInd.begin();
4874           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4875           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4876           {
4877             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4878             int nbn = vTool.NbFaceNodes( *ind );
4879             const SMDS_MeshElement * f = 0;
4880             if ( nbn == 3 )              ///// triangle
4881             {
4882               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4883               if ( !f ||
4884                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4885               {
4886                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4887                                                      nodes[ 1 ],
4888                                                      nodes[ 1 + nextShift ] };
4889                 if ( f )
4890                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4891                 else
4892                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4893                                                                newOrder[ 2 ] ));
4894               }
4895             }
4896             else if ( nbn == 4 )       ///// quadrangle
4897             {
4898               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4899               if ( !f ||
4900                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4901               {
4902                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4903                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4904                 if ( f )
4905                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4906                 else
4907                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4908                                                                newOrder[ 2 ], newOrder[ 3 ]));
4909               }
4910             }
4911             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4912             {
4913               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4914               if ( !f ||
4915                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4916               {
4917                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4918                                                      nodes[2],
4919                                                      nodes[2 + 2*nextShift],
4920                                                      nodes[3 - 2*nextShift],
4921                                                      nodes[3],
4922                                                      nodes[3 + 2*nextShift]};
4923                 if ( f )
4924                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4925                 else
4926                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4927                                                                newOrder[ 1 ],
4928                                                                newOrder[ 2 ],
4929                                                                newOrder[ 3 ],
4930                                                                newOrder[ 4 ],
4931                                                                newOrder[ 5 ] ));
4932               }
4933             }
4934             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4935             {
4936               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4937                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4938               if ( !f ||
4939                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4940               {
4941                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4942                                                      nodes[4 - 2*nextShift],
4943                                                      nodes[4],
4944                                                      nodes[4 + 2*nextShift],
4945                                                      nodes[1],
4946                                                      nodes[5 - 2*nextShift],
4947                                                      nodes[5],
4948                                                      nodes[5 + 2*nextShift] };
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4951                 else
4952                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4953                                                               newOrder[ 2 ], newOrder[ 3 ],
4954                                                               newOrder[ 4 ], newOrder[ 5 ],
4955                                                               newOrder[ 6 ], newOrder[ 7 ]));
4956               }
4957             }
4958             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4959             {
4960               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4961                                       SMDSAbs_Face, /*noMedium=*/false);
4962               if ( !f ||
4963                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4964               {
4965                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4966                                                      nodes[4 - 2*nextShift],
4967                                                      nodes[4],
4968                                                      nodes[4 + 2*nextShift],
4969                                                      nodes[1],
4970                                                      nodes[5 - 2*nextShift],
4971                                                      nodes[5],
4972                                                      nodes[5 + 2*nextShift],
4973                                                      nodes[8] };
4974                 if ( f )
4975                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4976                 else
4977                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978                                                               newOrder[ 2 ], newOrder[ 3 ],
4979                                                               newOrder[ 4 ], newOrder[ 5 ],
4980                                                               newOrder[ 6 ], newOrder[ 7 ],
4981                                                               newOrder[ 8 ]));
4982               }
4983             }
4984             else  //////// polygon
4985             {
4986               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4987               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4988               if ( !f ||
4989                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4990               {
4991                 if ( !vTool.IsForward() )
4992                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4993                 if ( f )
4994                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4995                 else
4996                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4997               }
4998             }
4999
5000             while ( srcElements.size() < myLastCreatedElems.size() )
5001               srcElements.push_back( *srcEdge );
5002
5003           }  // loop on free faces
5004
5005           // go to the next volume
5006           iVol = 0;
5007           while ( iVol++ < nbVolumesByStep ) v++;
5008
5009         } // loop on steps
5010       } // loop on volumes of one step
5011     } // sweep free links into faces
5012
5013     // Make a ceiling face with a normal external to a volume
5014
5015     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5016     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5017     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5018
5019     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5020       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5021       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5022     }
5023     if ( iF >= 0 )
5024     {
5025       lastVol.SetExternalNormal();
5026       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5027       const               int nbn = lastVol.NbFaceNodes( iF );
5028       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5029       if ( !hasFreeLinks ||
5030            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5031       {
5032         const vector<int>& interlace =
5033           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5034         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5035
5036         AddElement( nodeVec, anyFace.Init( elem ));
5037
5038         while ( srcElements.size() < myLastCreatedElems.size() )
5039           srcElements.push_back( elem );
5040       }
5041     }
5042   } // loop on swept elements
5043 }
5044
5045 //=======================================================================
5046 //function : RotationSweep
5047 //purpose  :
5048 //=======================================================================
5049
5050 SMESH_MeshEditor::PGroupIDs
5051 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5052                                 const gp_Ax1&      theAxis,
5053                                 const double       theAngle,
5054                                 const int          theNbSteps,
5055                                 const double       theTol,
5056                                 const bool         theMakeGroups,
5057                                 const bool         theMakeWalls)
5058 {
5059   ClearLastCreated();
5060
5061   setElemsFirst( theElemSets );
5062   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5063   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5064
5065   // source elements for each generated one
5066   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5067   srcElems.reserve( theElemSets[0].size() );
5068   srcNodes.reserve( theElemSets[1].size() );
5069
5070   gp_Trsf aTrsf;
5071   aTrsf.SetRotation( theAxis, theAngle );
5072   gp_Trsf aTrsf2;
5073   aTrsf2.SetRotation( theAxis, theAngle/2. );
5074
5075   gp_Lin aLine( theAxis );
5076   double aSqTol = theTol * theTol;
5077
5078   SMESHDS_Mesh* aMesh = GetMeshDS();
5079
5080   TNodeOfNodeListMap mapNewNodes;
5081   TElemOfVecOfNnlmiMap mapElemNewNodes;
5082   TTElemOfElemListMap newElemsMap;
5083
5084   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5085                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5086                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5087   // loop on theElemSets
5088   TIDSortedElemSet::iterator itElem;
5089   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5090   {
5091     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5092     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5093       const SMDS_MeshElement* elem = *itElem;
5094       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5095         continue;
5096       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5097       newNodesItVec.reserve( elem->NbNodes() );
5098
5099       // loop on elem nodes
5100       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5101       while ( itN->more() )
5102       {
5103         const SMDS_MeshNode* node = cast2Node( itN->next() );
5104
5105         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5106         double coord[3];
5107         aXYZ.Coord( coord[0], coord[1], coord[2] );
5108         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5109
5110         // check if a node has been already sweeped
5111         TNodeOfNodeListMapItr nIt =
5112           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5113         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5114         if ( listNewNodes.empty() )
5115         {
5116           // check if we are to create medium nodes between corner ones
5117           bool needMediumNodes = false;
5118           if ( isQuadraticMesh )
5119           {
5120             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5121             while (it->more() && !needMediumNodes )
5122             {
5123               const SMDS_MeshElement* invElem = it->next();
5124               if ( invElem != elem && !theElems.count( invElem )) continue;
5125               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5126               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5127                 needMediumNodes = true;
5128             }
5129           }
5130
5131           // make new nodes
5132           const SMDS_MeshNode * newNode = node;
5133           for ( int i = 0; i < theNbSteps; i++ ) {
5134             if ( !isOnAxis ) {
5135               if ( needMediumNodes )  // create a medium node
5136               {
5137                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5138                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5139                 myLastCreatedNodes.push_back(newNode);
5140                 srcNodes.push_back( node );
5141                 listNewNodes.push_back( newNode );
5142                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5143               }
5144               else {
5145                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5146               }
5147               // create a corner node
5148               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5149               myLastCreatedNodes.push_back(newNode);
5150               srcNodes.push_back( node );
5151               listNewNodes.push_back( newNode );
5152             }
5153             else {
5154               listNewNodes.push_back( newNode );
5155               // if ( needMediumNodes )
5156               //   listNewNodes.push_back( newNode );
5157             }
5158           }
5159         }
5160         newNodesItVec.push_back( nIt );
5161       }
5162       // make new elements
5163       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5164     }
5165   }
5166
5167   if ( theMakeWalls )
5168     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5169
5170   PGroupIDs newGroupIDs;
5171   if ( theMakeGroups )
5172     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5173
5174   return newGroupIDs;
5175 }
5176
5177 //=======================================================================
5178 //function : ExtrusParam
5179 //purpose  : standard construction
5180 //=======================================================================
5181
5182 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5183                                             const int                theNbSteps,
5184                                             const std::list<double>& theScales,
5185                                             const std::list<double>& theAngles,
5186                                             const gp_XYZ*            theBasePoint,
5187                                             const int                theFlags,
5188                                             const double             theTolerance):
5189   myDir( theStep ),
5190   myBaseP( Precision::Infinite(), 0, 0 ),
5191   myFlags( theFlags ),
5192   myTolerance( theTolerance ),
5193   myElemsToUse( NULL )
5194 {
5195   mySteps = new TColStd_HSequenceOfReal;
5196   const double stepSize = theStep.Magnitude();
5197   for (int i=1; i<=theNbSteps; i++ )
5198     mySteps->Append( stepSize );
5199
5200   if ( !theScales.empty() )
5201   {
5202     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5203       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5204
5205     // add medium scales
5206     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5207     myScales.reserve( theNbSteps * 2 );
5208     myScales.push_back( 0.5 * ( *s1 + 1. ));
5209     myScales.push_back( *s1 );
5210     for ( ; s2 != theScales.end(); s1 = s2++ )
5211     {
5212       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5213       myScales.push_back( *s2 );
5214     }
5215   }
5216
5217   if ( !theAngles.empty() )
5218   {
5219     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5220     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5221       linearAngleVariation( theNbSteps, angles );
5222
5223     // accumulate angles
5224     double angle = 0;
5225     int nbAngles = 0;
5226     std::list<double>::iterator a1 = angles.begin(), a2;
5227     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5228     {
5229       angle += *a1;
5230       *a1 = angle;
5231     }
5232     while ( nbAngles++ < theNbSteps )
5233       angles.push_back( angles.back() );
5234
5235     // add medium angles
5236     a2 = angles.begin(), a1 = a2++;
5237     myAngles.push_back( 0.5 * *a1 );
5238     myAngles.push_back( *a1 );
5239     for ( ; a2 != angles.end(); a1 = a2++ )
5240     {
5241       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5242       myAngles.push_back( *a2 );
5243     }
5244   }
5245
5246   if ( theBasePoint )
5247   {
5248     myBaseP = *theBasePoint;
5249   }
5250
5251   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5252       ( theTolerance > 0 ))
5253   {
5254     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5255   }
5256   else
5257   {
5258     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5259   }
5260 }
5261
5262 //=======================================================================
5263 //function : ExtrusParam
5264 //purpose  : steps are given explicitly
5265 //=======================================================================
5266
5267 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5268                                             Handle(TColStd_HSequenceOfReal) theSteps,
5269                                             const int                       theFlags,
5270                                             const double                    theTolerance):
5271   myDir( theDir ),
5272   mySteps( theSteps ),
5273   myFlags( theFlags ),
5274   myTolerance( theTolerance ),
5275   myElemsToUse( NULL )
5276 {
5277   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5278       ( theTolerance > 0 ))
5279   {
5280     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5281   }
5282   else
5283   {
5284     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5285   }
5286 }
5287
5288 //=======================================================================
5289 //function : ExtrusParam
5290 //purpose  : for extrusion by normal
5291 //=======================================================================
5292
5293 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5294                                             const int    theNbSteps,
5295                                             const int    theFlags,
5296                                             const int    theDim ):
5297   myDir( 1,0,0 ),
5298   mySteps( new TColStd_HSequenceOfReal ),
5299   myFlags( theFlags ),
5300   myTolerance( 0 ),
5301   myElemsToUse( NULL )
5302 {
5303   for (int i = 0; i < theNbSteps; i++ )
5304     mySteps->Append( theStepSize );
5305
5306   if ( theDim == 1 )
5307   {
5308     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5309   }
5310   else
5311   {
5312     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5313   }
5314 }
5315
5316 //=======================================================================
5317 //function : ExtrusParam
5318 //purpose  : for extrusion along path
5319 //=======================================================================
5320
5321 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5322                                             const gp_Pnt*                   theBasePoint,
5323                                             const std::list<double>&        theScales,
5324                                             const bool                      theMakeGroups )
5325   : myBaseP( Precision::Infinite(), 0, 0 ),
5326     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5327     myPathPoints( thePoints )
5328 {
5329   if ( theBasePoint )
5330   {
5331     myBaseP = theBasePoint->XYZ();
5332   }
5333
5334   if ( !theScales.empty() )
5335   {
5336     // add medium scales
5337     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5338     myScales.reserve( thePoints.size() * 2 );
5339     myScales.push_back( 0.5 * ( 1. + *s1 ));
5340     myScales.push_back( *s1 );
5341     for ( ; s2 != theScales.end(); s1 = s2++ )
5342     {
5343       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5344       myScales.push_back( *s2 );
5345     }
5346   }
5347
5348   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5349 }
5350
5351 //=======================================================================
5352 //function : ExtrusParam::SetElementsToUse
5353 //purpose  : stores elements to use for extrusion by normal, depending on
5354 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5355 //           define myBaseP for scaling
5356 //=======================================================================
5357
5358 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5359                                                       const TIDSortedElemSet& nodes )
5360 {
5361   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5362
5363   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5364   {
5365     myBaseP.SetCoord( 0.,0.,0. );
5366     TIDSortedElemSet newNodes;
5367
5368     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5369     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5370     {
5371       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5372       TIDSortedElemSet::const_iterator itElem = elements.begin();
5373       for ( ; itElem != elements.end(); itElem++ )
5374       {
5375         const SMDS_MeshElement* elem = *itElem;
5376         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5377         while ( itN->more() ) {
5378           const SMDS_MeshElement* node = itN->next();
5379           if ( newNodes.insert( node ).second )
5380             myBaseP += SMESH_NodeXYZ( node );
5381         }
5382       }
5383     }
5384     myBaseP /= newNodes.size();
5385   }
5386 }
5387
5388 //=======================================================================
5389 //function : ExtrusParam::beginStepIter
5390 //purpose  : prepare iteration on steps
5391 //=======================================================================
5392
5393 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5394 {
5395   myWithMediumNodes = withMediumNodes;
5396   myNextStep = 1;
5397   myCurSteps.clear();
5398 }
5399 //=======================================================================
5400 //function : ExtrusParam::moreSteps
5401 //purpose  : are there more steps?
5402 //=======================================================================
5403
5404 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5405 {
5406   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5407 }
5408 //=======================================================================
5409 //function : ExtrusParam::nextStep
5410 //purpose  : returns the next step
5411 //=======================================================================
5412
5413 double SMESH_MeshEditor::ExtrusParam::nextStep()
5414 {
5415   double res = 0;
5416   if ( !myCurSteps.empty() )
5417   {
5418     res = myCurSteps.back();
5419     myCurSteps.pop_back();
5420   }
5421   else if ( myNextStep <= mySteps->Length() )
5422   {
5423     myCurSteps.push_back( mySteps->Value( myNextStep ));
5424     ++myNextStep;
5425     if ( myWithMediumNodes )
5426     {
5427       myCurSteps.back() /= 2.;
5428       myCurSteps.push_back( myCurSteps.back() );
5429     }
5430     res = nextStep();
5431   }
5432   return res;
5433 }
5434
5435 //=======================================================================
5436 //function : ExtrusParam::makeNodesByDir
5437 //purpose  : create nodes for standard extrusion
5438 //=======================================================================
5439
5440 int SMESH_MeshEditor::ExtrusParam::
5441 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5442                 const SMDS_MeshNode*              srcNode,
5443                 std::list<const SMDS_MeshNode*> & newNodes,
5444                 const bool                        makeMediumNodes)
5445 {
5446   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5447
5448   int nbNodes = 0;
5449   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5450   {
5451     p += myDir.XYZ() * nextStep();
5452     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5453     newNodes.push_back( newNode );
5454   }
5455
5456   if ( !myScales.empty() || !myAngles.empty() )
5457   {
5458     gp_XYZ  center = myBaseP;
5459     gp_Ax1  ratationAxis( center, myDir );
5460     gp_Trsf rotation;
5461
5462     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5463     size_t i = !makeMediumNodes;
5464     for ( beginStepIter( makeMediumNodes );
5465           moreSteps();
5466           ++nIt, i += 1 + !makeMediumNodes )
5467     {
5468       center += myDir.XYZ() * nextStep();
5469
5470       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5471       bool moved = false;
5472       if ( i < myScales.size() )
5473       {
5474         xyz = ( myScales[i] * ( xyz - center )) + center;
5475         moved = true;
5476       }
5477       if ( !myAngles.empty() )
5478       {
5479         rotation.SetRotation( ratationAxis, myAngles[i] );
5480         rotation.Transforms( xyz );
5481         moved = true;
5482       }
5483       if ( moved )
5484         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5485       else
5486         break;
5487     }
5488   }
5489   return nbNodes;
5490 }
5491
5492 //=======================================================================
5493 //function : ExtrusParam::makeNodesByDirAndSew
5494 //purpose  : create nodes for standard extrusion with sewing
5495 //=======================================================================
5496
5497 int SMESH_MeshEditor::ExtrusParam::
5498 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5499                       const SMDS_MeshNode*              srcNode,
5500                       std::list<const SMDS_MeshNode*> & newNodes,
5501                       const bool                        makeMediumNodes)
5502 {
5503   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5504
5505   int nbNodes = 0;
5506   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5507   {
5508     P1 += myDir.XYZ() * nextStep();
5509
5510     // try to search in sequence of existing nodes
5511     // if myNodes.size()>0 we 'nave to use given sequence
5512     // else - use all nodes of mesh
5513     const SMDS_MeshNode * node = 0;
5514     if ( myNodes.Length() > 0 )
5515     {
5516       for ( int i = 1; i <= myNodes.Length(); i++ )
5517       {
5518         SMESH_NodeXYZ P2 = myNodes.Value(i);
5519         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5520         {
5521           node = myNodes.Value(i);
5522           break;
5523         }
5524       }
5525     }
5526     else
5527     {
5528       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5529       while(itn->more())
5530       {
5531         SMESH_NodeXYZ P2 = itn->next();
5532         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5533         {
5534           node = P2._node;
5535           break;
5536         }
5537       }
5538     }
5539
5540     if ( !node )
5541       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5542
5543     newNodes.push_back( node );
5544
5545   } // loop on steps
5546
5547   return nbNodes;
5548 }
5549
5550 //=======================================================================
5551 //function : ExtrusParam::makeNodesByNormal2D
5552 //purpose  : create nodes for extrusion using normals of faces
5553 //=======================================================================
5554
5555 int SMESH_MeshEditor::ExtrusParam::
5556 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5557                      const SMDS_MeshNode*              srcNode,
5558                      std::list<const SMDS_MeshNode*> & newNodes,
5559                      const bool                        makeMediumNodes)
5560 {
5561   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5562
5563   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5564
5565   // get normals to faces sharing srcNode
5566   vector< gp_XYZ > norms, baryCenters;
5567   gp_XYZ norm, avgNorm( 0,0,0 );
5568   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5569   while ( faceIt->more() )
5570   {
5571     const SMDS_MeshElement* face = faceIt->next();
5572     if ( myElemsToUse && !myElemsToUse->count( face ))
5573       continue;
5574     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5575     {
5576       norms.push_back( norm );
5577       avgNorm += norm;
5578       if ( !alongAvgNorm )
5579       {
5580         gp_XYZ bc(0,0,0);
5581         int nbN = 0;
5582         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5583           bc += SMESH_NodeXYZ( nIt->next() );
5584         baryCenters.push_back( bc / nbN );
5585       }
5586     }
5587   }
5588
5589   if ( norms.empty() ) return 0;
5590
5591   double normSize = avgNorm.Modulus();
5592   if ( normSize < std::numeric_limits<double>::min() )
5593     return 0;
5594
5595   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5596   {
5597     myDir = avgNorm;
5598     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5599   }
5600
5601   avgNorm /= normSize;
5602
5603   int nbNodes = 0;
5604   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5605   {
5606     gp_XYZ pNew = p;
5607     double stepSize = nextStep();
5608
5609     if ( norms.size() > 1 )
5610     {
5611       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5612       {
5613         // translate plane of a face
5614         baryCenters[ iF ] += norms[ iF ] * stepSize;
5615
5616         // find point of intersection of the face plane located at baryCenters[ iF ]
5617         // and avgNorm located at pNew
5618         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5619         double dot  = ( norms[ iF ] * avgNorm );
5620         if ( dot < std::numeric_limits<double>::min() )
5621           dot = stepSize * 1e-3;
5622         double step = -( norms[ iF ] * pNew + d ) / dot;
5623         pNew += step * avgNorm;
5624       }
5625     }
5626     else
5627     {
5628       pNew += stepSize * avgNorm;
5629     }
5630     p = pNew;
5631
5632     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5633     newNodes.push_back( newNode );
5634   }
5635   return nbNodes;
5636 }
5637
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByNormal1D
5640 //purpose  : create nodes for extrusion using normals of edges
5641 //=======================================================================
5642
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5645                      const SMDS_MeshNode*              srcNode,
5646                      std::list<const SMDS_MeshNode*> & newNodes,
5647                      const bool                        makeMediumNodes)
5648 {
5649   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5650   return 0;
5651 }
5652
5653 //=======================================================================
5654 //function : ExtrusParam::makeNodesAlongTrack
5655 //purpose  : create nodes for extrusion along path
5656 //=======================================================================
5657
5658 int SMESH_MeshEditor::ExtrusParam::
5659 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5660                      const SMDS_MeshNode*              srcNode,
5661                      std::list<const SMDS_MeshNode*> & newNodes,
5662                      const bool                        makeMediumNodes)
5663 {
5664   const Standard_Real aTolAng=1.e-4;
5665
5666   gp_Pnt aV0x = myBaseP;
5667   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5668
5669   const PathPoint& aPP0 = myPathPoints[0];
5670   gp_Pnt aP0x = aPP0.myPnt;
5671   gp_Dir aDT0x= aPP0.myTgt;
5672
5673   std::vector< gp_Pnt > centers;
5674   centers.reserve( NbSteps() * 2 );
5675
5676   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5677
5678   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5679   {
5680     const PathPoint&  aPP  = myPathPoints[j];
5681     const gp_Pnt&     aP1x = aPP.myPnt;
5682     const gp_Dir&    aDT1x = aPP.myTgt;
5683
5684     // Translation
5685     gp_Vec aV01x( aP0x, aP1x );
5686     aTrsf.SetTranslation( aV01x );
5687     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5688     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5689
5690     // rotation 1 [ T1,T0 ]
5691     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5692     if ( fabs( aAngleT1T0 ) > aTolAng )
5693     {
5694       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5695       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5696
5697       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5698     }
5699
5700     // rotation 2
5701     if ( aPP.myAngle != 0. )
5702     {
5703       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5704       aPN1 = aPN1.Transformed( aTrsfRot );
5705     }
5706
5707     // make new node
5708     if ( makeMediumNodes )
5709     {
5710       // create additional node
5711       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5712       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5713       newNodes.push_back( newNode );
5714
5715     }
5716     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5717     newNodes.push_back( newNode );
5718
5719     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5720     centers.push_back( aV1x );
5721
5722     aPN0 = aPN1;
5723     aP0x = aP1x;
5724     aV0x = aV1x;
5725     aDT0x = aDT1x;
5726   }
5727
5728   // scale
5729   if ( !myScales.empty() )
5730   {
5731     gp_Trsf aTrsfScale;
5732     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5733     for ( size_t i = !makeMediumNodes;
5734           i < myScales.size() && node != newNodes.end();
5735           i += ( 1 + !makeMediumNodes ), ++node )
5736     {
5737       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5738       gp_Pnt aN = SMESH_NodeXYZ( *node );
5739       gp_Pnt aP = aN.Transformed( aTrsfScale );
5740       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5741     }
5742   }
5743
5744   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5745 }
5746
5747 //=======================================================================
5748 //function : ExtrusionSweep
5749 //purpose  :
5750 //=======================================================================
5751
5752 SMESH_MeshEditor::PGroupIDs
5753 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5754                                   const gp_Vec&        theStep,
5755                                   const int            theNbSteps,
5756                                   TTElemOfElemListMap& newElemsMap,
5757                                   const int            theFlags,
5758                                   const double         theTolerance)
5759 {
5760   std::list<double> dummy;
5761   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5762                        theFlags, theTolerance );
5763   return ExtrusionSweep( theElems, aParams, newElemsMap );
5764 }
5765
5766 namespace
5767 {
5768
5769 //=======================================================================
5770 //function : getOriFactor
5771 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5772 //           edge curve orientation
5773 //=======================================================================
5774
5775   double getOriFactor( const TopoDS_Edge&   edge,
5776                        const SMDS_MeshNode* n1,
5777                        const SMDS_MeshNode* n2,
5778                        SMESH_MesherHelper&  helper)
5779   {
5780     double u1 = helper.GetNodeU( edge, n1, n2 );
5781     double u2 = helper.GetNodeU( edge, n2, n1 );
5782     return u1 < u2 ? 1. : -1.;
5783   }
5784 }
5785
5786 //=======================================================================
5787 //function : ExtrusionSweep
5788 //purpose  :
5789 //=======================================================================
5790
5791 SMESH_MeshEditor::PGroupIDs
5792 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5793                                   ExtrusParam&         theParams,
5794                                   TTElemOfElemListMap& newElemsMap)
5795 {
5796   ClearLastCreated();
5797
5798   setElemsFirst( theElemSets );
5799   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5800   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5801
5802   // source elements for each generated one
5803   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5804   srcElems.reserve( theElemSets[0].size() );
5805   srcNodes.reserve( theElemSets[1].size() );
5806
5807   const int nbSteps = theParams.NbSteps();
5808   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5809
5810   TNodeOfNodeListMap   mapNewNodes;
5811   TElemOfVecOfNnlmiMap mapElemNewNodes;
5812
5813   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5814                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5815                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5816   // loop on theElems
5817   TIDSortedElemSet::iterator itElem;
5818   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5819   {
5820     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5821     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5822     {
5823       // check element type
5824       const SMDS_MeshElement* elem = *itElem;
5825       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5826         continue;
5827
5828       const size_t nbNodes = elem->NbNodes();
5829       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5830       newNodesItVec.reserve( nbNodes );
5831
5832       // loop on elem nodes
5833       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5834       while ( itN->more() )
5835       {
5836         // check if a node has been already sweeped
5837         const SMDS_MeshNode* node = itN->next();
5838         TNodeOfNodeListMap::iterator nIt =
5839           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5840         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5841         if ( listNewNodes.empty() )
5842         {
5843           // make new nodes
5844
5845           // check if we are to create medium nodes between corner ones
5846           bool needMediumNodes = false;
5847           if ( isQuadraticMesh )
5848           {
5849             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5850             while (it->more() && !needMediumNodes )
5851             {
5852               const SMDS_MeshElement* invElem = it->next();
5853               if ( invElem != elem && !theElems.count( invElem )) continue;
5854               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5855               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5856                 needMediumNodes = true;
5857             }
5858           }
5859           // create nodes for all steps
5860           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5861           {
5862             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5863             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5864             {
5865               myLastCreatedNodes.push_back( *newNodesIt );
5866               srcNodes.push_back( node );
5867             }
5868           }
5869           else
5870           {
5871             if ( theParams.ToMakeBoundary() )
5872             {
5873               GetMeshDS()->Modified();
5874               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5875             }
5876             break; // newNodesItVec will be shorter than nbNodes
5877           }
5878         }
5879         newNodesItVec.push_back( nIt );
5880       }
5881       // make new elements
5882       if ( newNodesItVec.size() == nbNodes )
5883         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5884     }
5885   }
5886
5887   if ( theParams.ToMakeBoundary() ) {
5888     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5889   }
5890   PGroupIDs newGroupIDs;
5891   if ( theParams.ToMakeGroups() )
5892     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5893
5894   return newGroupIDs;
5895 }
5896
5897 //=======================================================================
5898 //function : ExtrusionAlongTrack
5899 //purpose  :
5900 //=======================================================================
5901 SMESH_MeshEditor::Extrusion_Error
5902 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5903                                        SMESH_Mesh*          theTrackMesh,
5904                                        SMDS_ElemIteratorPtr theTrackIterator,
5905                                        const SMDS_MeshNode* theN1,
5906                                        std::list<double>&   theAngles,
5907                                        const bool           theAngleVariation,
5908                                        std::list<double>&   theScales,
5909                                        const bool           theScaleVariation,
5910                                        const gp_Pnt*        theRefPoint,
5911                                        const bool           theMakeGroups)
5912 {
5913   ClearLastCreated();
5914
5915   // 1. Check data
5916   if ( theElements[0].empty() && theElements[1].empty() )
5917     return EXTR_NO_ELEMENTS;
5918
5919   ASSERT( theTrackMesh );
5920   if ( ! theTrackIterator || !theTrackIterator->more() )
5921     return EXTR_NO_ELEMENTS;
5922
5923   // 2. Get ordered nodes
5924   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5925   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5926   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5927   if ( branchEdges.empty() )
5928     return EXTR_PATH_NOT_EDGE;
5929
5930   if ( branchEdges.size() > 1 )
5931     return EXTR_BAD_PATH_SHAPE;
5932
5933   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5934   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5935   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5936     return EXTR_BAD_STARTING_NODE;
5937
5938   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5939   {
5940     // add medium nodes to pathNodes
5941     std::vector< const SMDS_MeshNode* >    pathNodes2;
5942     std::vector< const SMDS_MeshElement* > pathEdges2;
5943     pathNodes2.reserve( pathNodes.size() * 2 );
5944     pathEdges2.reserve( pathEdges.size() * 2 );
5945     for ( size_t i = 0; i < pathEdges.size(); ++i )
5946     {
5947       pathNodes2.push_back( pathNodes[i] );
5948       pathEdges2.push_back( pathEdges[i] );
5949       if ( pathEdges[i]->IsQuadratic() )
5950       {
5951         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5952         pathEdges2.push_back( pathEdges[i] );
5953       }
5954     }
5955     pathNodes2.push_back( pathNodes.back() );
5956     pathEdges.swap( pathEdges2 );
5957     pathNodes.swap( pathNodes2 );
5958   }
5959
5960   // 3. Get path data at pathNodes
5961
5962   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5963
5964   if ( theAngleVariation )
5965     linearAngleVariation( points.size()-1, theAngles );
5966   if ( theScaleVariation )
5967     linearScaleVariation( points.size()-1, theScales );
5968
5969   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5970   std::list<double>::iterator angle = theAngles.begin();
5971
5972   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5973
5974   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5975   std::map< int, double >::iterator id2factor;
5976   SMESH_MesherHelper pathHelper( *theTrackMesh );
5977   gp_Pnt p; gp_Vec tangent;
5978   const double tol2 = gp::Resolution() * gp::Resolution();
5979
5980   for ( size_t i = 0; i < pathNodes.size(); ++i )
5981   {
5982     ExtrusParam::PathPoint & point = points[ i ];
5983
5984     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5985
5986     if ( angle != theAngles.end() )
5987       point.myAngle = *angle++;
5988
5989     tangent.SetCoord( 0,0,0 );
5990     const int          shapeID = pathNodes[ i ]->GetShapeID();
5991     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
5992     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5993     switch ( shapeType )
5994     {
5995     case TopAbs_EDGE:
5996     {
5997       TopoDS_Edge edge = TopoDS::Edge( shape );
5998       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5999       if ( id2factor->second == 0 )
6000       {
6001         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6002         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6003       }
6004       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6005       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6006       curve->D1( u, p, tangent );
6007       tangent *= id2factor->second;
6008       break;
6009     }
6010     case TopAbs_VERTEX:
6011     {
6012       int nbEdges = 0;
6013       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6014       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6015       {
6016         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6017         for ( int di = -1; di <= 0; ++di )
6018         {
6019           size_t j = i + di;
6020           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6021           {
6022             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6023             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6024             if ( id2factor->second == 0 )
6025             {
6026               if ( j < i )
6027                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6028               else
6029                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6030             }
6031             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6032             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6033             gp_Vec du;
6034             curve->D1( u, p, du );
6035             double size2 = du.SquareMagnitude();
6036             if ( du.SquareMagnitude() > tol2 )
6037             {
6038               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6039               nbEdges++;
6040             }
6041             break;
6042           }
6043         }
6044       }
6045       if ( nbEdges > 0 )
6046         break;
6047     }
6048     default:
6049     {
6050       for ( int di = -1; di <= 1; di += 2 )
6051       {
6052         size_t j = i + di;
6053         if ( j < pathNodes.size() )
6054         {
6055           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6056           double size2 = dir.SquareMagnitude();
6057           if ( size2 > tol2 )
6058             tangent += dir.Divided( Sqrt( size2 )) * di;
6059         }
6060       }
6061     }
6062     } // switch ( shapeType )
6063
6064     if ( tangent.SquareMagnitude() < tol2 )
6065       return EXTR_CANT_GET_TANGENT;
6066
6067     point.myTgt = tangent;
6068
6069   } // loop on pathNodes
6070
6071
6072   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6073   TTElemOfElemListMap newElemsMap;
6074
6075   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6076
6077   return EXTR_OK;
6078 }
6079
6080 //=======================================================================
6081 //function : linearAngleVariation
6082 //purpose  : spread values over nbSteps
6083 //=======================================================================
6084
6085 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6086                                             list<double>& Angles)
6087 {
6088   int nbAngles = Angles.size();
6089   if( nbSteps > nbAngles && nbAngles > 0 )
6090   {
6091     vector<double> theAngles(nbAngles);
6092     theAngles.assign( Angles.begin(), Angles.end() );
6093
6094     list<double> res;
6095     double rAn2St = double( nbAngles ) / double( nbSteps );
6096     double angPrev = 0, angle;
6097     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6098     {
6099       double angCur = rAn2St * ( iSt+1 );
6100       double angCurFloor  = floor( angCur );
6101       double angPrevFloor = floor( angPrev );
6102       if ( angPrevFloor == angCurFloor )
6103         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6104       else {
6105         int iP = int( angPrevFloor );
6106         double angPrevCeil = ceil(angPrev);
6107         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6108
6109         int iC = int( angCurFloor );
6110         if ( iC < nbAngles )
6111           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6112
6113         iP = int( angPrevCeil );
6114         while ( iC-- > iP )
6115           angle += theAngles[ iC ];
6116       }
6117       res.push_back(angle);
6118       angPrev = angCur;
6119     }
6120     Angles.swap( res );
6121   }
6122 }
6123
6124 //=======================================================================
6125 //function : linearScaleVariation
6126 //purpose  : spread values over nbSteps 
6127 //=======================================================================
6128
6129 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6130                                             std::list<double>& theScales)
6131 {
6132   int nbScales = theScales.size();
6133   std::vector<double> myScales;
6134   myScales.reserve( theNbSteps );
6135   std::list<double>::const_iterator scale = theScales.begin();
6136   double prevScale = 1.0;
6137   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6138   {
6139     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6140     int    stDelta = Max( 1, iStep - myScales.size());
6141     double scDelta = ( *scale - prevScale ) / stDelta;
6142     for ( int iStep = 0; iStep < stDelta; ++iStep )
6143     {
6144       myScales.push_back( prevScale + scDelta );
6145       prevScale = myScales.back();
6146     }
6147     prevScale = *scale;
6148   }
6149   theScales.assign( myScales.begin(), myScales.end() );
6150 }
6151
6152 //================================================================================
6153 /*!
6154  * \brief Move or copy theElements applying theTrsf to their nodes
6155  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6156  *  \param theTrsf - transformation to apply
6157  *  \param theCopy - if true, create translated copies of theElems
6158  *  \param theMakeGroups - if true and theCopy, create translated groups
6159  *  \param theTargetMesh - mesh to copy translated elements into
6160  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6161  */
6162 //================================================================================
6163
6164 SMESH_MeshEditor::PGroupIDs
6165 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6166                              const gp_Trsf&     theTrsf,
6167                              const bool         theCopy,
6168                              const bool         theMakeGroups,
6169                              SMESH_Mesh*        theTargetMesh)
6170 {
6171   ClearLastCreated();
6172   myLastCreatedElems.reserve( theElems.size() );
6173
6174   bool needReverse = false;
6175   string groupPostfix;
6176   switch ( theTrsf.Form() ) {
6177   case gp_PntMirror:
6178     needReverse = true;
6179     groupPostfix = "mirrored";
6180     break;
6181   case gp_Ax1Mirror:
6182     groupPostfix = "mirrored";
6183     break;
6184   case gp_Ax2Mirror:
6185     needReverse = true;
6186     groupPostfix = "mirrored";
6187     break;
6188   case gp_Rotation:
6189     groupPostfix = "rotated";
6190     break;
6191   case gp_Translation:
6192     groupPostfix = "translated";
6193     break;
6194   case gp_Scale:
6195     groupPostfix = "scaled";
6196     break;
6197   case gp_CompoundTrsf: // different scale by axis
6198     groupPostfix = "scaled";
6199     break;
6200   default:
6201     needReverse = false;
6202     groupPostfix = "transformed";
6203   }
6204
6205   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6206   SMESHDS_Mesh* aMesh    = GetMeshDS();
6207
6208   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6209   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6210   SMESH_MeshEditor::ElemFeatures elemType;
6211
6212   // map old node to new one
6213   TNodeNodeMap nodeMap;
6214
6215   // elements sharing moved nodes; those of them which have all
6216   // nodes mirrored but are not in theElems are to be reversed
6217   TIDSortedElemSet inverseElemSet;
6218
6219   // source elements for each generated one
6220   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6221
6222   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6223   TIDSortedElemSet orphanNode;
6224
6225   if ( theElems.empty() ) // transform the whole mesh
6226   {
6227     // add all elements
6228     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6229     while ( eIt->more() ) theElems.insert( eIt->next() );
6230     // add orphan nodes
6231     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6232     while ( nIt->more() )
6233     {
6234       const SMDS_MeshNode* node = nIt->next();
6235       if ( node->NbInverseElements() == 0)
6236         orphanNode.insert( node );
6237     }
6238   }
6239
6240   // loop on elements to transform nodes : first orphan nodes then elems
6241   TIDSortedElemSet::iterator itElem;
6242   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6243   for (int i=0; i<2; i++)
6244     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6245     {
6246       const SMDS_MeshElement* elem = *itElem;
6247       if ( !elem )
6248         continue;
6249
6250       // loop on elem nodes
6251       double coord[3];
6252       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6253       while ( itN->more() )
6254       {
6255         const SMDS_MeshNode* node = cast2Node( itN->next() );
6256         // check if a node has been already transformed
6257         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6258           nodeMap.insert( make_pair ( node, node ));
6259         if ( !n2n_isnew.second )
6260           continue;
6261
6262         node->GetXYZ( coord );
6263         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6264         if ( theTargetMesh ) {
6265           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6266           n2n_isnew.first->second = newNode;
6267           myLastCreatedNodes.push_back(newNode);
6268           srcNodes.push_back( node );
6269         }
6270         else if ( theCopy ) {
6271           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6272           n2n_isnew.first->second = newNode;
6273           myLastCreatedNodes.push_back(newNode);
6274           srcNodes.push_back( node );
6275         }
6276         else {
6277           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6278           // node position on shape becomes invalid
6279           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6280             ( SMDS_SpacePosition::originSpacePosition() );
6281         }
6282
6283         // keep inverse elements
6284         if ( !theCopy && !theTargetMesh && needReverse ) {
6285           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6286           while ( invElemIt->more() ) {
6287             const SMDS_MeshElement* iel = invElemIt->next();
6288             inverseElemSet.insert( iel );
6289           }
6290         }
6291       }
6292     } // loop on elems in { &orphanNode, &theElems };
6293
6294   // either create new elements or reverse mirrored ones
6295   if ( !theCopy && !needReverse && !theTargetMesh )
6296     return PGroupIDs();
6297
6298   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6299
6300   // Replicate or reverse elements
6301
6302   std::vector<int> iForw;
6303   vector<const SMDS_MeshNode*> nodes;
6304   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6305   {
6306     const SMDS_MeshElement* elem = *itElem;
6307     if ( !elem ) continue;
6308
6309     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6310     size_t               nbNodes  = elem->NbNodes();
6311     if ( geomType == SMDSGeom_NONE ) continue; // node
6312
6313     nodes.resize( nbNodes );
6314
6315     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6316     {
6317       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6318       if ( !aPolyedre )
6319         continue;
6320       nodes.clear();
6321       bool allTransformed = true;
6322       int nbFaces = aPolyedre->NbFaces();
6323       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6324       {
6325         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6326         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6327         {
6328           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6329           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6330           if ( nodeMapIt == nodeMap.end() )
6331             allTransformed = false; // not all nodes transformed
6332           else
6333             nodes.push_back((*nodeMapIt).second);
6334         }
6335         if ( needReverse && allTransformed )
6336           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6337       }
6338       if ( !allTransformed )
6339         continue; // not all nodes transformed
6340     }
6341     else // ----------------------- the rest element types
6342     {
6343       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6344       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6345       const vector<int>&    i = needReverse ? iRev : iForw;
6346
6347       // find transformed nodes
6348       size_t iNode = 0;
6349       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6350       while ( itN->more() ) {
6351         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6352         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6353         if ( nodeMapIt == nodeMap.end() )
6354           break; // not all nodes transformed
6355         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6356       }
6357       if ( iNode != nbNodes )
6358         continue; // not all nodes transformed
6359     }
6360
6361     if ( editor ) {
6362       // copy in this or a new mesh
6363       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6364         srcElems.push_back( elem );
6365     }
6366     else {
6367       // reverse element as it was reversed by transformation
6368       if ( nbNodes > 2 )
6369         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6370     }
6371
6372   } // loop on elements
6373
6374   if ( editor && editor != this )
6375     myLastCreatedElems.swap( editor->myLastCreatedElems );
6376
6377   PGroupIDs newGroupIDs;
6378
6379   if ( ( theMakeGroups && theCopy ) ||
6380        ( theMakeGroups && theTargetMesh ) )
6381     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6382
6383   return newGroupIDs;
6384 }
6385
6386 //================================================================================
6387 /*!
6388  * \brief Make an offset mesh from a source 2D mesh
6389  *  \param [in] theElements - source faces
6390  *  \param [in] theValue - offset value
6391  *  \param [out] theTgtMesh - a mesh to add offset elements to
6392  *  \param [in] theMakeGroups - to generate groups
6393  *  \return PGroupIDs - IDs of created groups. NULL means failure
6394  */
6395 //================================================================================
6396
6397 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6398                                                       const double       theValue,
6399                                                       SMESH_Mesh*        theTgtMesh,
6400                                                       const bool         theMakeGroups,
6401                                                       const bool         theCopyElements,
6402                                                       const bool         theFixSelfIntersection)
6403 {
6404   SMESHDS_Mesh*    meshDS = GetMeshDS();
6405   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6406   SMESH_MeshEditor tgtEditor( theTgtMesh );
6407
6408   SMDS_ElemIteratorPtr eIt;
6409   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6410   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6411
6412   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6413   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6414   std::unique_ptr< SMDS_Mesh > offsetMesh
6415     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6416                                    theFixSelfIntersection,
6417                                    new2OldFaces, new2OldNodes ));
6418   if ( offsetMesh->NbElements() == 0 )
6419     return PGroupIDs(); // MakeOffset() failed
6420
6421
6422   if ( theTgtMesh == myMesh && !theCopyElements )
6423   {
6424     // clear the source elements
6425     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6426     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6427     while ( eIt->more() )
6428       meshDS->RemoveFreeElement( eIt->next(), 0 );
6429   }
6430
6431   // offsetMesh->Modified();
6432   // offsetMesh->CompactMesh(); // make IDs start from 1
6433
6434   // source elements for each generated one
6435   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6436   srcElems.reserve( new2OldFaces.size() );
6437   srcNodes.reserve( new2OldNodes.size() );
6438
6439   ClearLastCreated();
6440   myLastCreatedElems.reserve( new2OldFaces.size() );
6441   myLastCreatedNodes.reserve( new2OldNodes.size() );
6442
6443   // copy offsetMesh to theTgtMesh
6444
6445   int idShift = meshDS->MaxNodeID();
6446   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6447     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6448     {
6449 #ifndef _DEBUG_
6450       if ( n->NbInverseElements() > 0 )
6451 #endif
6452       {
6453         const SMDS_MeshNode* n2 =
6454           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6455         myLastCreatedNodes.push_back( n2 );
6456         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6457       }
6458     }
6459
6460   ElemFeatures elemType;
6461   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6462     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6463     {
6464       elemType.Init( f );
6465       elemType.myNodes.clear();
6466       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6467       {
6468         const SMDS_MeshNode* n2 = nIt->next();
6469         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6470       }
6471       tgtEditor.AddElement( elemType.myNodes, elemType );
6472       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6473     }
6474
6475   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6476
6477   PGroupIDs newGroupIDs;
6478   if ( theMakeGroups )
6479     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6480   else
6481     newGroupIDs.reset( new std::list< int > );
6482
6483   return newGroupIDs;
6484 }
6485
6486 //=======================================================================
6487 /*!
6488  * \brief Create groups of elements made during transformation
6489  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6490  *  \param elemGens - elements making corresponding myLastCreatedElems
6491  *  \param postfix - to push_back to names of new groups
6492  *  \param targetMesh - mesh to create groups in
6493  *  \param topPresent - is there are "top" elements that are created by sweeping
6494  */
6495 //=======================================================================
6496
6497 SMESH_MeshEditor::PGroupIDs
6498 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6499                                  const SMESH_SequenceOfElemPtr& elemGens,
6500                                  const std::string&             postfix,
6501                                  SMESH_Mesh*                    targetMesh,
6502                                  const bool                     topPresent)
6503 {
6504   PGroupIDs newGroupIDs( new list<int> );
6505   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6506
6507   // Sort existing groups by types and collect their names
6508
6509   // containers to store an old group and generated new ones;
6510   // 1st new group is for result elems of different type than a source one;
6511   // 2nd new group is for same type result elems ("top" group at extrusion)
6512   using boost::tuple;
6513   using boost::make_tuple;
6514   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6515   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6516   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6517   // group names
6518   set< string > groupNames;
6519
6520   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6521   if ( !groupIt->more() ) return newGroupIDs;
6522
6523   int newGroupID = mesh->GetGroupIds().back()+1;
6524   while ( groupIt->more() )
6525   {
6526     SMESH_Group * group = groupIt->next();
6527     if ( !group ) continue;
6528     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6529     if ( !groupDS || groupDS->IsEmpty() ) continue;
6530     groupNames.insert    ( group->GetName() );
6531     groupDS->SetStoreName( group->GetName() );
6532     const SMDSAbs_ElementType type = groupDS->GetType();
6533     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6534     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6535     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6536     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6537   }
6538
6539   // Loop on nodes and elements to add them in new groups
6540
6541   vector< const SMDS_MeshElement* > resultElems;
6542   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6543   {
6544     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6545     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6546     if ( gens.size() != elems.size() )
6547       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6548
6549     // loop on created elements
6550     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6551     {
6552       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6553       if ( !sourceElem ) {
6554         MESSAGE("generateGroups(): NULL source element");
6555         continue;
6556       }
6557       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6558       if ( groupsOldNew.empty() ) { // no groups of this type at all
6559         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6560           ++iElem; // skip all elements made by sourceElem
6561         continue;
6562       }
6563       // collect all elements made by the iElem-th sourceElem
6564       resultElems.clear();
6565       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6566         if ( resElem != sourceElem )
6567           resultElems.push_back( resElem );
6568       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6569         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6570           if ( resElem != sourceElem )
6571             resultElems.push_back( resElem );
6572
6573       const SMDS_MeshElement* topElem = 0;
6574       if ( isNodes ) // there must be a top element
6575       {
6576         topElem = resultElems.back();
6577         resultElems.pop_back();
6578       }
6579       else
6580       {
6581         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6582         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6583           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6584           {
6585             topElem = *resElemIt;
6586             *resElemIt = 0; // erase *resElemIt
6587             break;
6588           }
6589       }
6590       // add resultElems to groups originted from ones the sourceElem belongs to
6591       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6592       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6593       {
6594         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6595         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6596         {
6597           // fill in a new group
6598           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6599           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6600           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6601             if ( *resElemIt )
6602               newGroup.Add( *resElemIt );
6603
6604           // fill a "top" group
6605           if ( topElem )
6606           {
6607             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6608             newTopGroup.Add( topElem );
6609           }
6610         }
6611       }
6612     } // loop on created elements
6613   }// loop on nodes and elements
6614
6615   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6616
6617   list<int> topGrouIds;
6618   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6619   {
6620     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6621     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6622                                       orderedOldNewGroups[i]->get<2>() };
6623     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6624     {
6625       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6626       if ( newGroupDS->IsEmpty() )
6627       {
6628         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6629       }
6630       else
6631       {
6632         // set group type
6633         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6634
6635         // make a name
6636         const bool isTop = ( topPresent &&
6637                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6638                              is2nd );
6639
6640         string name = oldGroupDS->GetStoreName();
6641         { // remove trailing whitespaces (issue 22599)
6642           size_t size = name.size();
6643           while ( size > 1 && isspace( name[ size-1 ]))
6644             --size;
6645           if ( size != name.size() )
6646           {
6647             name.resize( size );
6648             oldGroupDS->SetStoreName( name.c_str() );
6649           }
6650         }
6651         if ( !targetMesh ) {
6652           string suffix = ( isTop ? "top": postfix.c_str() );
6653           name += "_";
6654           name += suffix;
6655           int nb = 1;
6656           while ( !groupNames.insert( name ).second ) // name exists
6657             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6658         }
6659         else if ( isTop ) {
6660           name += "_top";
6661         }
6662         newGroupDS->SetStoreName( name.c_str() );
6663
6664         // make a SMESH_Groups
6665         mesh->AddGroup( newGroupDS );
6666         if ( isTop )
6667           topGrouIds.push_back( newGroupDS->GetID() );
6668         else
6669           newGroupIDs->push_back( newGroupDS->GetID() );
6670       }
6671     }
6672   }
6673   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6674
6675   return newGroupIDs;
6676 }
6677
6678 //================================================================================
6679 /*!
6680  *  * \brief Return list of group of nodes close to each other within theTolerance
6681  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6682  *  *        an Octree algorithm
6683  *  \param [in,out] theNodes - the nodes to treat
6684  *  \param [in]     theTolerance - the tolerance
6685  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6686  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6687  *         corner and medium nodes in separate groups
6688  */
6689 //================================================================================
6690
6691 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6692                                             const double         theTolerance,
6693                                             TListOfListOfNodes & theGroupsOfNodes,
6694                                             bool                 theSeparateCornersAndMedium)
6695 {
6696   ClearLastCreated();
6697
6698   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6699        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6700        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6701     theSeparateCornersAndMedium = false;
6702
6703   TIDSortedNodeSet& corners = theNodes;
6704   TIDSortedNodeSet  medium;
6705
6706   if ( theNodes.empty() ) // get all nodes in the mesh
6707   {
6708     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6709     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6710     if ( theSeparateCornersAndMedium )
6711       while ( nIt->more() )
6712       {
6713         const SMDS_MeshNode* n = nIt->next();
6714         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6715         nodeSet->insert( nodeSet->end(), n );
6716       }
6717     else
6718       while ( nIt->more() )
6719         theNodes.insert( theNodes.end(), nIt->next() );
6720   }
6721   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6722   {
6723     TIDSortedNodeSet::iterator nIt = corners.begin();
6724     while ( nIt != corners.end() )
6725       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6726       {
6727         medium.insert( medium.end(), *nIt );
6728         corners.erase( nIt++ );
6729       }
6730       else
6731       {
6732         ++nIt;
6733       }
6734   }
6735
6736   if ( !corners.empty() )
6737     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6738   if ( !medium.empty() )
6739     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6740 }
6741
6742 //=======================================================================
6743 //function : SimplifyFace
6744 //purpose  : split a chain of nodes into several closed chains
6745 //=======================================================================
6746
6747 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6748                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6749                                     vector<int>&                         quantities) const
6750 {
6751   int nbNodes = faceNodes.size();
6752   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6753     --nbNodes;
6754   if ( nbNodes < 3 )
6755     return 0;
6756   size_t prevNbQuant = quantities.size();
6757
6758   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6759   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6760   map< const SMDS_MeshNode*, int >::iterator nInd;
6761
6762   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6763   simpleNodes.push_back( faceNodes[0] );
6764   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6765   {
6766     if ( faceNodes[ iCur ] != simpleNodes.back() )
6767     {
6768       int index = simpleNodes.size();
6769       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6770       int prevIndex = nInd->second;
6771       if ( prevIndex < index )
6772       {
6773         // a sub-loop found
6774         int loopLen = index - prevIndex;
6775         if ( loopLen > 2 )
6776         {
6777           // store the sub-loop
6778           quantities.push_back( loopLen );
6779           for ( int i = prevIndex; i < index; i++ )
6780             poly_nodes.push_back( simpleNodes[ i ]);
6781         }
6782         simpleNodes.resize( prevIndex+1 );
6783       }
6784       else
6785       {
6786         simpleNodes.push_back( faceNodes[ iCur ]);
6787       }
6788     }
6789   }
6790
6791   if ( simpleNodes.size() > 2 )
6792   {
6793     quantities.push_back( simpleNodes.size() );
6794     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6795   }
6796
6797   return quantities.size() - prevNbQuant;
6798 }
6799
6800 //=======================================================================
6801 //function : MergeNodes
6802 //purpose  : In each group, the cdr of nodes are substituted by the first one
6803 //           in all elements.
6804 //=======================================================================
6805
6806 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6807                                    const bool           theAvoidMakingHoles)
6808 {
6809   ClearLastCreated();
6810
6811   SMESHDS_Mesh* mesh = GetMeshDS();
6812
6813   TNodeNodeMap nodeNodeMap; // node to replace - new node
6814   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6815   list< int > rmElemIds, rmNodeIds;
6816   vector< ElemFeatures > newElemDefs;
6817
6818   // Fill nodeNodeMap and elems
6819
6820   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6821   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6822   {
6823     list<const SMDS_MeshNode*>& nodes = *grIt;
6824     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6825     const SMDS_MeshNode* nToKeep = *nIt;
6826     for ( ++nIt; nIt != nodes.end(); nIt++ )
6827     {
6828       const SMDS_MeshNode* nToRemove = *nIt;
6829       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6830       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6831       while ( invElemIt->more() ) {
6832         const SMDS_MeshElement* elem = invElemIt->next();
6833         elems.insert(elem);
6834       }
6835     }
6836   }
6837
6838   // Apply recursive replacements (BUG 0020185)
6839   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6840   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6841   {
6842     const SMDS_MeshNode* nToKeep = nnIt->second;
6843     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6844     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6845     {
6846       nToKeep = nnIt_i->second;
6847       nnIt->second = nToKeep;
6848       nnIt_i = nodeNodeMap.find( nToKeep );
6849     }
6850   }
6851
6852   if ( theAvoidMakingHoles )
6853   {
6854     // find elements whose topology changes
6855
6856     vector<const SMDS_MeshElement*> pbElems;
6857     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6858     for ( ; eIt != elems.end(); ++eIt )
6859     {
6860       const SMDS_MeshElement* elem = *eIt;
6861       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6862       while ( itN->more() )
6863       {
6864         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6865         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6866         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6867         {
6868           // several nodes of elem stick
6869           pbElems.push_back( elem );
6870           break;
6871         }
6872       }
6873     }
6874     // exclude from merge nodes causing spoiling element
6875     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6876     {
6877       bool nodesExcluded = false;
6878       for ( size_t i = 0; i < pbElems.size(); ++i )
6879       {
6880         size_t prevNbMergeNodes = nodeNodeMap.size();
6881         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6882              prevNbMergeNodes < nodeNodeMap.size() )
6883           nodesExcluded = true;
6884       }
6885       if ( !nodesExcluded )
6886         break;
6887     }
6888   }
6889
6890   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6891   {
6892     const SMDS_MeshNode* nToRemove = nnIt->first;
6893     const SMDS_MeshNode* nToKeep   = nnIt->second;
6894     if ( nToRemove != nToKeep )
6895     {
6896       rmNodeIds.push_back( nToRemove->GetID() );
6897       AddToSameGroups( nToKeep, nToRemove, mesh );
6898       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6899       // w/o creating node in place of merged ones.
6900       SMDS_PositionPtr pos = nToRemove->GetPosition();
6901       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6902         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6903           sm->SetIsAlwaysComputed( true );
6904     }
6905   }
6906
6907   // Change element nodes or remove an element
6908
6909   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6910   for ( ; eIt != elems.end(); eIt++ )
6911   {
6912     const SMDS_MeshElement* elem = *eIt;
6913     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6914
6915     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6916     if ( !keepElem )
6917       rmElemIds.push_back( elem->GetID() );
6918
6919     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6920     {
6921       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6922                                                & newElemDefs[i].myNodes[0],
6923                                                newElemDefs[i].myNodes.size() ))
6924       {
6925         if ( i == 0 )
6926         {
6927           newElemDefs[i].SetID( elem->GetID() );
6928           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6929           if ( !keepElem ) rmElemIds.pop_back();
6930         }
6931         else
6932         {
6933           newElemDefs[i].SetID( -1 );
6934         }
6935         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6936         if ( sm && newElem )
6937           sm->AddElement( newElem );
6938         if ( elem != newElem )
6939           ReplaceElemInGroups( elem, newElem, mesh );
6940       }
6941     }
6942   }
6943
6944   // Remove bad elements, then equal nodes (order important)
6945   Remove( rmElemIds, /*isNodes=*/false );
6946   Remove( rmNodeIds, /*isNodes=*/true );
6947
6948   return;
6949 }
6950
6951 //=======================================================================
6952 //function : applyMerge
6953 //purpose  : Compute new connectivity of an element after merging nodes
6954 //  \param [in] elems - the element
6955 //  \param [out] newElemDefs - definition(s) of result element(s)
6956 //  \param [inout] nodeNodeMap - nodes to merge
6957 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6958 //              after merging (but not degenerated), removes nodes causing
6959 //              the invalidity from \a nodeNodeMap.
6960 //  \return bool - true if the element should be removed
6961 //=======================================================================
6962
6963 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6964                                    vector< ElemFeatures >& newElemDefs,
6965                                    TNodeNodeMap&           nodeNodeMap,
6966                                    const bool              avoidMakingHoles )
6967 {
6968   bool toRemove = false; // to remove elem
6969   int nbResElems = 1;    // nb new elements
6970
6971   newElemDefs.resize(nbResElems);
6972   newElemDefs[0].Init( elem );
6973   newElemDefs[0].myNodes.clear();
6974
6975   set<const SMDS_MeshNode*> nodeSet;
6976   vector< const SMDS_MeshNode*>   curNodes;
6977   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6978   vector<int> iRepl;
6979
6980   const        int  nbNodes = elem->NbNodes();
6981   SMDSAbs_EntityType entity = elem->GetEntityType();
6982
6983   curNodes.resize( nbNodes );
6984   uniqueNodes.resize( nbNodes );
6985   iRepl.resize( nbNodes );
6986   int iUnique = 0, iCur = 0, nbRepl = 0;
6987
6988   // Get new seq of nodes
6989
6990   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991   while ( itN->more() )
6992   {
6993     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6994
6995     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6996     if ( nnIt != nodeNodeMap.end() ) {
6997       n = (*nnIt).second;
6998     }
6999     curNodes[ iCur ] = n;
7000     bool isUnique = nodeSet.insert( n ).second;
7001     if ( isUnique )
7002       uniqueNodes[ iUnique++ ] = n;
7003     else
7004       iRepl[ nbRepl++ ] = iCur;
7005     iCur++;
7006   }
7007
7008   // Analyse element topology after replacement
7009
7010   int nbUniqueNodes = nodeSet.size();
7011   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7012   {
7013     toRemove = true;
7014     nbResElems = 0;
7015
7016     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7017     {
7018       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7019       int nbCorners = nbNodes / 2;
7020       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7021       {
7022         int iNext = ( iCur + 1 ) % nbCorners;
7023         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7024         {
7025           int iMedium = iCur + nbCorners;
7026           vector< const SMDS_MeshNode* >::iterator i =
7027             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7028                        uniqueNodes.end(),
7029                        curNodes[ iMedium ]);
7030           if ( i != uniqueNodes.end() )
7031           {
7032             --nbUniqueNodes;
7033             for ( ; i+1 != uniqueNodes.end(); ++i )
7034               *i = *(i+1);
7035           }
7036         }
7037       }
7038     }
7039
7040     switch ( entity )
7041     {
7042     case SMDSEntity_Polygon:
7043     case SMDSEntity_Quad_Polygon: // Polygon
7044     {
7045       ElemFeatures* elemType = & newElemDefs[0];
7046       const bool isQuad = elemType->myIsQuad;
7047       if ( isQuad )
7048         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7049           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7050
7051       // a polygon can divide into several elements
7052       vector<const SMDS_MeshNode *> polygons_nodes;
7053       vector<int> quantities;
7054       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7055       newElemDefs.resize( nbResElems );
7056       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7057       {
7058         ElemFeatures* elemType = & newElemDefs[iface];
7059         if ( iface ) elemType->Init( elem );
7060
7061         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7062         int nbNewNodes = quantities[iface];
7063         face_nodes.assign( polygons_nodes.begin() + inode,
7064                            polygons_nodes.begin() + inode + nbNewNodes );
7065         inode += nbNewNodes;
7066         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7067         {
7068           bool isValid = ( nbNewNodes % 2 == 0 );
7069           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7070             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7071           elemType->SetQuad( isValid );
7072           if ( isValid ) // put medium nodes after corners
7073             SMDS_MeshCell::applyInterlaceRev
7074               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7075                                                     nbNewNodes ), face_nodes );
7076         }
7077         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7078       }
7079       nbUniqueNodes = newElemDefs[0].myNodes.size();
7080       break;
7081     } // Polygon
7082
7083     case SMDSEntity_Polyhedra: // Polyhedral volume
7084     {
7085       if ( nbUniqueNodes >= 4 )
7086       {
7087         // each face has to be analyzed in order to check volume validity
7088         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7089         {
7090           int nbFaces = aPolyedre->NbFaces();
7091
7092           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7093           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7094           vector<const SMDS_MeshNode *>  faceNodes;
7095           poly_nodes.clear();
7096           quantities.clear();
7097
7098           for (int iface = 1; iface <= nbFaces; iface++)
7099           {
7100             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7101             faceNodes.resize( nbFaceNodes );
7102             for (int inode = 1; inode <= nbFaceNodes; inode++)
7103             {
7104               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7105               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7106               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7107                 faceNode = (*nnIt).second;
7108               faceNodes[inode - 1] = faceNode;
7109             }
7110             SimplifyFace(faceNodes, poly_nodes, quantities);
7111           }
7112
7113           if ( quantities.size() > 3 )
7114           {
7115             // TODO: remove coincident faces
7116             nbResElems = 1;
7117             nbUniqueNodes = newElemDefs[0].myNodes.size();
7118           }
7119         }
7120       }
7121     }
7122     break;
7123
7124     // Regular elements
7125     // TODO not all the possible cases are solved. Find something more generic?
7126     case SMDSEntity_Edge: //////// EDGE
7127     case SMDSEntity_Triangle: //// TRIANGLE
7128     case SMDSEntity_Quad_Triangle:
7129     case SMDSEntity_Tetra:
7130     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7131     {
7132       break;
7133     }
7134     case SMDSEntity_Quad_Edge:
7135     {
7136       break;
7137     }
7138     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7139     {
7140       if ( nbUniqueNodes < 3 )
7141         toRemove = true;
7142       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7143         toRemove = true; // opposite nodes stick
7144       else
7145         toRemove = false;
7146       break;
7147     }
7148     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7149     {
7150       //   1    5    2
7151       //    +---+---+
7152       //    |       |
7153       //   4+       +6
7154       //    |       |
7155       //    +---+---+
7156       //   0    7    3
7157       if ( nbUniqueNodes == 6 &&
7158            iRepl[0] < 4       &&
7159            ( nbRepl == 1 || iRepl[1] >= 4 ))
7160       {
7161         toRemove = false;
7162       }
7163       break;
7164     }
7165     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7166     {
7167       //   1    5    2
7168       //    +---+---+
7169       //    |       |
7170       //   4+  8+   +6
7171       //    |       |
7172       //    +---+---+
7173       //   0    7    3
7174       if ( nbUniqueNodes == 7 &&
7175            iRepl[0] < 4       &&
7176            ( nbRepl == 1 || iRepl[1] != 8 ))
7177       {
7178         toRemove = false;
7179       }
7180       break;
7181     }
7182     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7183     {
7184       if ( nbUniqueNodes == 4 ) {
7185         // ---------------------------------> tetrahedron
7186         if ( curNodes[3] == curNodes[4] &&
7187              curNodes[3] == curNodes[5] ) {
7188           // top nodes stick
7189           toRemove = false;
7190         }
7191         else if ( curNodes[0] == curNodes[1] &&
7192                   curNodes[0] == curNodes[2] ) {
7193           // bottom nodes stick: set a top before
7194           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7195           uniqueNodes[ 0 ] = curNodes [ 5 ];
7196           uniqueNodes[ 1 ] = curNodes [ 4 ];
7197           uniqueNodes[ 2 ] = curNodes [ 3 ];
7198           toRemove = false;
7199         }
7200         else if (( curNodes[0] == curNodes[3] ) +
7201                  ( curNodes[1] == curNodes[4] ) +
7202                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7203           // a lateral face turns into a line
7204           toRemove = false;
7205         }
7206       }
7207       else if ( nbUniqueNodes == 5 ) {
7208         // PENTAHEDRON --------------------> pyramid
7209         if ( curNodes[0] == curNodes[3] )
7210         {
7211           uniqueNodes[ 0 ] = curNodes[ 1 ];
7212           uniqueNodes[ 1 ] = curNodes[ 4 ];
7213           uniqueNodes[ 2 ] = curNodes[ 5 ];
7214           uniqueNodes[ 3 ] = curNodes[ 2 ];
7215           uniqueNodes[ 4 ] = curNodes[ 0 ];
7216           toRemove = false;
7217         }
7218         if ( curNodes[1] == curNodes[4] )
7219         {
7220           uniqueNodes[ 0 ] = curNodes[ 0 ];
7221           uniqueNodes[ 1 ] = curNodes[ 2 ];
7222           uniqueNodes[ 2 ] = curNodes[ 5 ];
7223           uniqueNodes[ 3 ] = curNodes[ 3 ];
7224           uniqueNodes[ 4 ] = curNodes[ 1 ];
7225           toRemove = false;
7226         }
7227         if ( curNodes[2] == curNodes[5] )
7228         {
7229           uniqueNodes[ 0 ] = curNodes[ 0 ];
7230           uniqueNodes[ 1 ] = curNodes[ 3 ];
7231           uniqueNodes[ 2 ] = curNodes[ 4 ];
7232           uniqueNodes[ 3 ] = curNodes[ 1 ];
7233           uniqueNodes[ 4 ] = curNodes[ 2 ];
7234           toRemove = false;
7235         }
7236       }
7237       break;
7238     }
7239     case SMDSEntity_Hexa:
7240     {
7241       //////////////////////////////////// HEXAHEDRON
7242       SMDS_VolumeTool hexa (elem);
7243       hexa.SetExternalNormal();
7244       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7245         //////////////////////// HEX ---> tetrahedron
7246         for ( int iFace = 0; iFace < 6; iFace++ ) {
7247           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7248           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7249               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7250               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7251             // one face turns into a point ...
7252             int  pickInd = ind[ 0 ];
7253             int iOppFace = hexa.GetOppFaceIndex( iFace );
7254             ind = hexa.GetFaceNodesIndices( iOppFace );
7255             int nbStick = 0;
7256             uniqueNodes.clear();
7257             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7258               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7259                 nbStick++;
7260               else
7261                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7262             }
7263             if ( nbStick == 1 ) {
7264               // ... and the opposite one - into a triangle.
7265               // set a top node
7266               uniqueNodes.push_back( curNodes[ pickInd ]);
7267               toRemove = false;
7268             }
7269             break;
7270           }
7271         }
7272       }
7273       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7274         //////////////////////// HEX ---> prism
7275         int nbTria = 0, iTria[3];
7276         const int *ind; // indices of face nodes
7277         // look for triangular faces
7278         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7279           ind = hexa.GetFaceNodesIndices( iFace );
7280           TIDSortedNodeSet faceNodes;
7281           for ( iCur = 0; iCur < 4; iCur++ )
7282             faceNodes.insert( curNodes[ind[iCur]] );
7283           if ( faceNodes.size() == 3 )
7284             iTria[ nbTria++ ] = iFace;
7285         }
7286         // check if triangles are opposite
7287         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7288         {
7289           // set nodes of the bottom triangle
7290           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7291           vector<int> indB;
7292           for ( iCur = 0; iCur < 4; iCur++ )
7293             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7294               indB.push_back( ind[iCur] );
7295           if ( !hexa.IsForward() )
7296             std::swap( indB[0], indB[2] );
7297           for ( iCur = 0; iCur < 3; iCur++ )
7298             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7299           // set nodes of the top triangle
7300           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7301           for ( iCur = 0; iCur < 3; ++iCur )
7302             for ( int j = 0; j < 4; ++j )
7303               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7304               {
7305                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7306                 break;
7307               }
7308           toRemove = false;
7309           break;
7310         }
7311       }
7312       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7313         //////////////////// HEXAHEDRON ---> pyramid
7314         for ( int iFace = 0; iFace < 6; iFace++ ) {
7315           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7316           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7317               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7318               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7319             // one face turns into a point ...
7320             int iOppFace = hexa.GetOppFaceIndex( iFace );
7321             ind = hexa.GetFaceNodesIndices( iOppFace );
7322             uniqueNodes.clear();
7323             for ( iCur = 0; iCur < 4; iCur++ ) {
7324               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7325                 break;
7326               else
7327                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7328             }
7329             if ( uniqueNodes.size() == 4 ) {
7330               // ... and the opposite one is a quadrangle
7331               // set a top node
7332               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7333               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7334               toRemove = false;
7335             }
7336             break;
7337           }
7338         }
7339       }
7340
7341       if ( toRemove && nbUniqueNodes > 4 ) {
7342         ////////////////// HEXAHEDRON ---> polyhedron
7343         hexa.SetExternalNormal();
7344         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7345         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7346         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7347         quantities.reserve( 6 );     quantities.clear();
7348         for ( int iFace = 0; iFace < 6; iFace++ )
7349         {
7350           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7351           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7352                curNodes[ind[1]] == curNodes[ind[3]] )
7353           {
7354             quantities.clear();
7355             break; // opposite nodes stick
7356           }
7357           nodeSet.clear();
7358           for ( iCur = 0; iCur < 4; iCur++ )
7359           {
7360             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7361               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7362           }
7363           if ( nodeSet.size() < 3 )
7364             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7365           else
7366             quantities.push_back( nodeSet.size() );
7367         }
7368         if ( quantities.size() >= 4 )
7369         {
7370           nbResElems = 1;
7371           nbUniqueNodes = poly_nodes.size();
7372           newElemDefs[0].SetPoly(true);
7373         }
7374       }
7375       break;
7376     } // case HEXAHEDRON
7377
7378     default:
7379       toRemove = true;
7380
7381     } // switch ( entity )
7382
7383     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7384     {
7385       // erase from nodeNodeMap nodes whose merge spoils elem
7386       vector< const SMDS_MeshNode* > noMergeNodes;
7387       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7388       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7389         nodeNodeMap.erase( noMergeNodes[i] );
7390     }
7391     
7392   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7393
7394   uniqueNodes.resize( nbUniqueNodes );
7395
7396   if ( !toRemove && nbResElems == 0 )
7397     nbResElems = 1;
7398
7399   newElemDefs.resize( nbResElems );
7400
7401   return !toRemove;
7402 }
7403
7404
7405 // ========================================================
7406 // class   : ComparableElement
7407 // purpose : allow comparing elements basing on their nodes
7408 // ========================================================
7409
7410 class ComparableElement : public boost::container::flat_set< int >
7411 {
7412   typedef boost::container::flat_set< int >  int_set;
7413
7414   const SMDS_MeshElement* myElem;
7415   int                     mySumID;
7416   mutable int             myGroupID;
7417
7418 public:
7419
7420   ComparableElement( const SMDS_MeshElement* theElem ):
7421     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7422   {
7423     this->reserve( theElem->NbNodes() );
7424     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7425     {
7426       int id = nodeIt->next()->GetID();
7427       mySumID += id;
7428       this->insert( id );
7429     }
7430   }
7431
7432   const SMDS_MeshElement* GetElem() const { return myElem; }
7433
7434   int& GroupID() const { return myGroupID; }
7435   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7436
7437   ComparableElement( const ComparableElement& theSource ) // move copy
7438   {
7439     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7440     (int_set&) (*this ) = boost::move( src );
7441     myElem    = src.myElem;
7442     mySumID   = src.mySumID;
7443     myGroupID = src.myGroupID;
7444   }
7445
7446   static int HashCode(const ComparableElement& se, int limit )
7447   {
7448     return ::HashCode( se.mySumID, limit );
7449   }
7450   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7451   {
7452     return ( se1 == se2 );
7453   }
7454
7455 };
7456
7457 //=======================================================================
7458 //function : FindEqualElements
7459 //purpose  : Return list of group of elements built on the same nodes.
7460 //           Search among theElements or in the whole mesh if theElements is empty
7461 //=======================================================================
7462
7463 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7464                                           TListOfListOfElementsID & theGroupsOfElementsID )
7465 {
7466   ClearLastCreated();
7467
7468   SMDS_ElemIteratorPtr elemIt;
7469   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7470   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7471
7472   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7473   typedef std::list<int>                                          TGroupOfElems;
7474   TMapOfElements               mapOfElements;
7475   std::vector< TGroupOfElems > arrayOfGroups;
7476   TGroupOfElems                groupOfElems;
7477
7478   while ( elemIt->more() )
7479   {
7480     const SMDS_MeshElement* curElem = elemIt->next();
7481     ComparableElement      compElem = curElem;
7482     // check uniqueness
7483     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7484     if ( elemInSet.GetElem() != curElem ) // coincident elem
7485     {
7486       int& iG = elemInSet.GroupID();
7487       if ( iG < 0 )
7488       {
7489         iG = arrayOfGroups.size();
7490         arrayOfGroups.push_back( groupOfElems );
7491         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7492       }
7493       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7494     }
7495   }
7496
7497   groupOfElems.clear();
7498   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7499   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7500   {
7501     if ( groupIt->size() > 1 ) {
7502       //groupOfElems.sort(); -- theElements are sorted already
7503       theGroupsOfElementsID.emplace_back( *groupIt );
7504     }
7505   }
7506 }
7507
7508 //=======================================================================
7509 //function : MergeElements
7510 //purpose  : In each given group, substitute all elements by the first one.
7511 //=======================================================================
7512
7513 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7514 {
7515   ClearLastCreated();
7516
7517   typedef list<int> TListOfIDs;
7518   TListOfIDs rmElemIds; // IDs of elems to remove
7519
7520   SMESHDS_Mesh* aMesh = GetMeshDS();
7521
7522   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7523   while ( groupsIt != theGroupsOfElementsID.end() ) {
7524     TListOfIDs& aGroupOfElemID = *groupsIt;
7525     aGroupOfElemID.sort();
7526     int elemIDToKeep = aGroupOfElemID.front();
7527     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7528     aGroupOfElemID.pop_front();
7529     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7530     while ( idIt != aGroupOfElemID.end() ) {
7531       int elemIDToRemove = *idIt;
7532       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7533       // add the kept element in groups of removed one (PAL15188)
7534       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7535       rmElemIds.push_back( elemIDToRemove );
7536       ++idIt;
7537     }
7538     ++groupsIt;
7539   }
7540
7541   Remove( rmElemIds, false );
7542 }
7543
7544 //=======================================================================
7545 //function : MergeEqualElements
7546 //purpose  : Remove all but one of elements built on the same nodes.
7547 //=======================================================================
7548
7549 void SMESH_MeshEditor::MergeEqualElements()
7550 {
7551   TIDSortedElemSet aMeshElements; /* empty input ==
7552                                      to merge equal elements in the whole mesh */
7553   TListOfListOfElementsID aGroupsOfElementsID;
7554   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7555   MergeElements( aGroupsOfElementsID );
7556 }
7557
7558 //=======================================================================
7559 //function : findAdjacentFace
7560 //purpose  :
7561 //=======================================================================
7562
7563 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7564                                                 const SMDS_MeshNode* n2,
7565                                                 const SMDS_MeshElement* elem)
7566 {
7567   TIDSortedElemSet elemSet, avoidSet;
7568   if ( elem )
7569     avoidSet.insert ( elem );
7570   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7571 }
7572
7573 //=======================================================================
7574 //function : findSegment
7575 //purpose  : Return a mesh segment by two nodes one of which can be medium
7576 //=======================================================================
7577
7578 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7579                                            const SMDS_MeshNode* n2)
7580 {
7581   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7582   while ( it->more() )
7583   {
7584     const SMDS_MeshElement* seg = it->next();
7585     if ( seg->GetNodeIndex( n2 ) >= 0 )
7586       return seg;
7587   }
7588   return 0;
7589 }
7590
7591 //=======================================================================
7592 //function : FindFreeBorder
7593 //purpose  :
7594 //=======================================================================
7595
7596 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7597
7598 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7599                                        const SMDS_MeshNode*             theSecondNode,
7600                                        const SMDS_MeshNode*             theLastNode,
7601                                        list< const SMDS_MeshNode* > &   theNodes,
7602                                        list< const SMDS_MeshElement* >& theFaces)
7603 {
7604   if ( !theFirstNode || !theSecondNode )
7605     return false;
7606   // find border face between theFirstNode and theSecondNode
7607   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7608   if ( !curElem )
7609     return false;
7610
7611   theFaces.push_back( curElem );
7612   theNodes.push_back( theFirstNode );
7613   theNodes.push_back( theSecondNode );
7614
7615   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7616   //TIDSortedElemSet foundElems;
7617   bool needTheLast = ( theLastNode != 0 );
7618
7619   vector<const SMDS_MeshNode*> nodes;
7620   
7621   while ( nStart != theLastNode ) {
7622     if ( nStart == theFirstNode )
7623       return !needTheLast;
7624
7625     // find all free border faces sharing nStart
7626
7627     list< const SMDS_MeshElement* > curElemList;
7628     list< const SMDS_MeshNode* >    nStartList;
7629     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7630     while ( invElemIt->more() ) {
7631       const SMDS_MeshElement* e = invElemIt->next();
7632       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7633       {
7634         // get nodes
7635         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7636                       SMDS_MeshElement::iterator() );
7637         nodes.push_back( nodes[ 0 ]);
7638
7639         // check 2 links
7640         int iNode = 0, nbNodes = nodes.size() - 1;
7641         for ( iNode = 0; iNode < nbNodes; iNode++ )
7642           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7643                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7644               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7645           {
7646             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7647             curElemList.push_back( e );
7648           }
7649       }
7650     }
7651     // analyse the found
7652
7653     int nbNewBorders = curElemList.size();
7654     if ( nbNewBorders == 0 ) {
7655       // no free border furthermore
7656       return !needTheLast;
7657     }
7658     else if ( nbNewBorders == 1 ) {
7659       // one more element found
7660       nIgnore = nStart;
7661       nStart = nStartList.front();
7662       curElem = curElemList.front();
7663       theFaces.push_back( curElem );
7664       theNodes.push_back( nStart );
7665     }
7666     else {
7667       // several continuations found
7668       list< const SMDS_MeshElement* >::iterator curElemIt;
7669       list< const SMDS_MeshNode* >::iterator nStartIt;
7670       // check if one of them reached the last node
7671       if ( needTheLast ) {
7672         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7673              curElemIt!= curElemList.end();
7674              curElemIt++, nStartIt++ )
7675           if ( *nStartIt == theLastNode ) {
7676             theFaces.push_back( *curElemIt );
7677             theNodes.push_back( *nStartIt );
7678             return true;
7679           }
7680       }
7681       // find the best free border by the continuations
7682       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7683       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7684       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7685            curElemIt!= curElemList.end();
7686            curElemIt++, nStartIt++ )
7687       {
7688         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7689         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7690         // find one more free border
7691         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7692           cNL->clear();
7693           cFL->clear();
7694         }
7695         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7696           // choice: clear a worse one
7697           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7698           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7699           contNodes[ iWorse ].clear();
7700           contFaces[ iWorse ].clear();
7701         }
7702       }
7703       if ( contNodes[0].empty() && contNodes[1].empty() )
7704         return false;
7705
7706       // push_back the best free border
7707       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7708       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7709       //theNodes.pop_back(); // remove nIgnore
7710       theNodes.pop_back(); // remove nStart
7711       //theFaces.pop_back(); // remove curElem
7712       theNodes.splice( theNodes.end(), *cNL );
7713       theFaces.splice( theFaces.end(), *cFL );
7714       return true;
7715
7716     } // several continuations found
7717   } // while ( nStart != theLastNode )
7718
7719   return true;
7720 }
7721
7722 //=======================================================================
7723 //function : CheckFreeBorderNodes
7724 //purpose  : Return true if the tree nodes are on a free border
7725 //=======================================================================
7726
7727 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7728                                             const SMDS_MeshNode* theNode2,
7729                                             const SMDS_MeshNode* theNode3)
7730 {
7731   list< const SMDS_MeshNode* > nodes;
7732   list< const SMDS_MeshElement* > faces;
7733   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7734 }
7735
7736 //=======================================================================
7737 //function : SewFreeBorder
7738 //purpose  :
7739 //warning  : for border-to-side sewing theSideSecondNode is considered as
7740 //           the last side node and theSideThirdNode is not used
7741 //=======================================================================
7742
7743 SMESH_MeshEditor::Sew_Error
7744 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7745                                  const SMDS_MeshNode* theBordSecondNode,
7746                                  const SMDS_MeshNode* theBordLastNode,
7747                                  const SMDS_MeshNode* theSideFirstNode,
7748                                  const SMDS_MeshNode* theSideSecondNode,
7749                                  const SMDS_MeshNode* theSideThirdNode,
7750                                  const bool           theSideIsFreeBorder,
7751                                  const bool           toCreatePolygons,
7752                                  const bool           toCreatePolyedrs)
7753 {
7754   ClearLastCreated();
7755
7756   Sew_Error aResult = SEW_OK;
7757
7758   // ====================================
7759   //    find side nodes and elements
7760   // ====================================
7761
7762   list< const SMDS_MeshNode* >    nSide[ 2 ];
7763   list< const SMDS_MeshElement* > eSide[ 2 ];
7764   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7765   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7766
7767   // Free border 1
7768   // --------------
7769   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7770                       nSide[0], eSide[0])) {
7771     MESSAGE(" Free Border 1 not found " );
7772     aResult = SEW_BORDER1_NOT_FOUND;
7773   }
7774   if (theSideIsFreeBorder) {
7775     // Free border 2
7776     // --------------
7777     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7778                         nSide[1], eSide[1])) {
7779       MESSAGE(" Free Border 2 not found " );
7780       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7781     }
7782   }
7783   if ( aResult != SEW_OK )
7784     return aResult;
7785
7786   if (!theSideIsFreeBorder) {
7787     // Side 2
7788     // --------------
7789
7790     // -------------------------------------------------------------------------
7791     // Algo:
7792     // 1. If nodes to merge are not coincident, move nodes of the free border
7793     //    from the coord sys defined by the direction from the first to last
7794     //    nodes of the border to the correspondent sys of the side 2
7795     // 2. On the side 2, find the links most co-directed with the correspondent
7796     //    links of the free border
7797     // -------------------------------------------------------------------------
7798
7799     // 1. Since sewing may break if there are volumes to split on the side 2,
7800     //    we won't move nodes but just compute new coordinates for them
7801     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7802     TNodeXYZMap nBordXYZ;
7803     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7804     list< const SMDS_MeshNode* >::iterator nBordIt;
7805
7806     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7807     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7808     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7809     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7810     double tol2 = 1.e-8;
7811     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7812     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7813       // Need node movement.
7814
7815       // find X and Z axes to create trsf
7816       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7817       gp_Vec X = Zs ^ Zb;
7818       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7819         // Zb || Zs
7820         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7821
7822       // coord systems
7823       gp_Ax3 toBordAx( Pb1, Zb, X );
7824       gp_Ax3 fromSideAx( Ps1, Zs, X );
7825       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7826       // set trsf
7827       gp_Trsf toBordSys, fromSide2Sys;
7828       toBordSys.SetTransformation( toBordAx );
7829       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7830       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7831
7832       // move
7833       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7834         const SMDS_MeshNode* n = *nBordIt;
7835         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7836         toBordSys.Transforms( xyz );
7837         fromSide2Sys.Transforms( xyz );
7838         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7839       }
7840     }
7841     else {
7842       // just insert nodes XYZ in the nBordXYZ map
7843       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7844         const SMDS_MeshNode* n = *nBordIt;
7845         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7846       }
7847     }
7848
7849     // 2. On the side 2, find the links most co-directed with the correspondent
7850     //    links of the free border
7851
7852     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7853     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7854     sideNodes.push_back( theSideFirstNode );
7855
7856     bool hasVolumes = false;
7857     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7858     set<long> foundSideLinkIDs, checkedLinkIDs;
7859     SMDS_VolumeTool volume;
7860     //const SMDS_MeshNode* faceNodes[ 4 ];
7861
7862     const SMDS_MeshNode*    sideNode;
7863     const SMDS_MeshElement* sideElem  = 0;
7864     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7865     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7866     nBordIt = bordNodes.begin();
7867     nBordIt++;
7868     // border node position and border link direction to compare with
7869     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7870     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7871     // choose next side node by link direction or by closeness to
7872     // the current border node:
7873     bool searchByDir = ( *nBordIt != theBordLastNode );
7874     do {
7875       // find the next node on the Side 2
7876       sideNode = 0;
7877       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7878       long linkID;
7879       checkedLinkIDs.clear();
7880       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7881
7882       // loop on inverse elements of current node (prevSideNode) on the Side 2
7883       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7884       while ( invElemIt->more() )
7885       {
7886         const SMDS_MeshElement* elem = invElemIt->next();
7887         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7888         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7889         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7890         bool isVolume = volume.Set( elem );
7891         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7892         if ( isVolume ) // --volume
7893           hasVolumes = true;
7894         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7895           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7896           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7897           while ( nIt->more() ) {
7898             nodes[ iNode ] = cast2Node( nIt->next() );
7899             if ( nodes[ iNode++ ] == prevSideNode )
7900               iPrevNode = iNode - 1;
7901           }
7902           // there are 2 links to check
7903           nbNodes = 2;
7904         }
7905         else // --edge
7906           continue;
7907         // loop on links, to be precise, on the second node of links
7908         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7909           const SMDS_MeshNode* n = nodes[ iNode ];
7910           if ( isVolume ) {
7911             if ( !volume.IsLinked( n, prevSideNode ))
7912               continue;
7913           }
7914           else {
7915             if ( iNode ) // a node before prevSideNode
7916               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7917             else         // a node after prevSideNode
7918               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7919           }
7920           // check if this link was already used
7921           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7922           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7923           if (!isJustChecked &&
7924               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7925           {
7926             // test a link geometrically
7927             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7928             bool linkIsBetter = false;
7929             double dot = 0.0, dist = 0.0;
7930             if ( searchByDir ) { // choose most co-directed link
7931               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7932               linkIsBetter = ( dot > maxDot );
7933             }
7934             else { // choose link with the node closest to bordPos
7935               dist = ( nextXYZ - bordPos ).SquareModulus();
7936               linkIsBetter = ( dist < minDist );
7937             }
7938             if ( linkIsBetter ) {
7939               maxDot = dot;
7940               minDist = dist;
7941               linkID = iLink;
7942               sideNode = n;
7943               sideElem = elem;
7944             }
7945           }
7946         }
7947       } // loop on inverse elements of prevSideNode
7948
7949       if ( !sideNode ) {
7950         MESSAGE(" Can't find path by links of the Side 2 ");
7951         return SEW_BAD_SIDE_NODES;
7952       }
7953       sideNodes.push_back( sideNode );
7954       sideElems.push_back( sideElem );
7955       foundSideLinkIDs.insert ( linkID );
7956       prevSideNode = sideNode;
7957
7958       if ( *nBordIt == theBordLastNode )
7959         searchByDir = false;
7960       else {
7961         // find the next border link to compare with
7962         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7963         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7964         // move to next border node if sideNode is before forward border node (bordPos)
7965         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7966           prevBordNode = *nBordIt;
7967           nBordIt++;
7968           bordPos = nBordXYZ[ *nBordIt ];
7969           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7970           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7971         }
7972       }
7973     }
7974     while ( sideNode != theSideSecondNode );
7975
7976     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7977       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7978       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7979     }
7980   } // end nodes search on the side 2
7981
7982   // ============================
7983   // sew the border to the side 2
7984   // ============================
7985
7986   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
7987   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7988
7989   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7990   if ( toMergeConformal && toCreatePolygons )
7991   {
7992     // do not merge quadrangles if polygons are OK (IPAL0052824)
7993     eIt[0] = eSide[0].begin();
7994     eIt[1] = eSide[1].begin();
7995     bool allQuads[2] = { true, true };
7996     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7997       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7998         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7999     }
8000     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8001   }
8002
8003   TListOfListOfNodes nodeGroupsToMerge;
8004   if (( toMergeConformal ) ||
8005       ( theSideIsFreeBorder && !theSideThirdNode )) {
8006
8007     // all nodes are to be merged
8008
8009     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8010          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8011          nIt[0]++, nIt[1]++ )
8012     {
8013       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8014       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8015       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8016     }
8017   }
8018   else {
8019
8020     // insert new nodes into the border and the side to get equal nb of segments
8021
8022     // get normalized parameters of nodes on the borders
8023     vector< double > param[ 2 ];
8024     param[0].resize( maxNbNodes );
8025     param[1].resize( maxNbNodes );
8026     int iNode, iBord;
8027     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8028       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8029       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8030       const SMDS_MeshNode* nPrev = *nIt;
8031       double bordLength = 0;
8032       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8033         const SMDS_MeshNode* nCur = *nIt;
8034         gp_XYZ segment (nCur->X() - nPrev->X(),
8035                         nCur->Y() - nPrev->Y(),
8036                         nCur->Z() - nPrev->Z());
8037         double segmentLen = segment.Modulus();
8038         bordLength += segmentLen;
8039         param[ iBord ][ iNode ] = bordLength;
8040         nPrev = nCur;
8041       }
8042       // normalize within [0,1]
8043       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8044         param[ iBord ][ iNode ] /= bordLength;
8045       }
8046     }
8047
8048     // loop on border segments
8049     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8050     int i[ 2 ] = { 0, 0 };
8051     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8052     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8053
8054     // element can be split while iterating on border if it has two edges in the border
8055     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8056     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8057
8058     TElemOfNodeListMap insertMap;
8059     TElemOfNodeListMap::iterator insertMapIt;
8060     // insertMap is
8061     // key:   elem to insert nodes into
8062     // value: 2 nodes to insert between + nodes to be inserted
8063     do {
8064       bool next[ 2 ] = { false, false };
8065
8066       // find min adjacent segment length after sewing
8067       double nextParam = 10., prevParam = 0;
8068       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8069         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8070           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8071         if ( i[ iBord ] > 0 )
8072           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8073       }
8074       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8075       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8076       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8077
8078       // choose to insert or to merge nodes
8079       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8080       if ( Abs( du ) <= minSegLen * 0.2 ) {
8081         // merge
8082         // ------
8083         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8084         const SMDS_MeshNode* n0 = *nIt[0];
8085         const SMDS_MeshNode* n1 = *nIt[1];
8086         nodeGroupsToMerge.back().push_back( n1 );
8087         nodeGroupsToMerge.back().push_back( n0 );
8088         // position of node of the border changes due to merge
8089         param[ 0 ][ i[0] ] += du;
8090         // move n1 for the sake of elem shape evaluation during insertion.
8091         // n1 will be removed by MergeNodes() anyway
8092         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8093         next[0] = next[1] = true;
8094       }
8095       else {
8096         // insert
8097         // ------
8098         int intoBord = ( du < 0 ) ? 0 : 1;
8099         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8100         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8101         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8102         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8103         if ( intoBord == 1 ) {
8104           // move node of the border to be on a link of elem of the side
8105           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8106           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8107           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8108           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8109         }
8110         elemReplaceMapIt = elemReplaceMap.find( elem );
8111         if ( elemReplaceMapIt != elemReplaceMap.end() )
8112           elem = elemReplaceMapIt->second;
8113
8114         insertMapIt = insertMap.find( elem );
8115         bool  notFound = ( insertMapIt == insertMap.end() );
8116         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8117         if ( otherLink ) {
8118           // insert into another link of the same element:
8119           // 1. perform insertion into the other link of the elem
8120           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8121           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8122           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8123           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8124           // 2. perform insertion into the link of adjacent faces
8125           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8126             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8127           }
8128           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8129             InsertNodesIntoLink( seg, n12, n22, nodeList );
8130           }
8131           if (toCreatePolyedrs) {
8132             // perform insertion into the links of adjacent volumes
8133             UpdateVolumes(n12, n22, nodeList);
8134           }
8135           // 3. find an element appeared on n1 and n2 after the insertion
8136           insertMap.erase( insertMapIt );
8137           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8138           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8139           elem = elem2;
8140         }
8141         if ( notFound || otherLink ) {
8142           // add element and nodes of the side into the insertMap
8143           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8144           (*insertMapIt).second.push_back( n1 );
8145           (*insertMapIt).second.push_back( n2 );
8146         }
8147         // add node to be inserted into elem
8148         (*insertMapIt).second.push_back( nIns );
8149         next[ 1 - intoBord ] = true;
8150       }
8151
8152       // go to the next segment
8153       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8154         if ( next[ iBord ] ) {
8155           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8156             eIt[ iBord ]++;
8157           nPrev[ iBord ] = *nIt[ iBord ];
8158           nIt[ iBord ]++; i[ iBord ]++;
8159         }
8160       }
8161     }
8162     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8163
8164     // perform insertion of nodes into elements
8165
8166     for (insertMapIt = insertMap.begin();
8167          insertMapIt != insertMap.end();
8168          insertMapIt++ )
8169     {
8170       const SMDS_MeshElement* elem = (*insertMapIt).first;
8171       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8172       if ( nodeList.size() < 3 ) continue;
8173       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8174       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8175
8176       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8177
8178       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8179         InsertNodesIntoLink( seg, n1, n2, nodeList );
8180       }
8181
8182       if ( !theSideIsFreeBorder ) {
8183         // look for and insert nodes into the faces adjacent to elem
8184         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8185           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8186         }
8187       }
8188       if (toCreatePolyedrs) {
8189         // perform insertion into the links of adjacent volumes
8190         UpdateVolumes(n1, n2, nodeList);
8191       }
8192     }
8193   } // end: insert new nodes
8194
8195   MergeNodes ( nodeGroupsToMerge );
8196
8197
8198   // Remove coincident segments
8199
8200   // get new segments
8201   TIDSortedElemSet segments;
8202   SMESH_SequenceOfElemPtr newFaces;
8203   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8204   {
8205     if ( !myLastCreatedElems[i] ) continue;
8206     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8207       segments.insert( segments.end(), myLastCreatedElems[i] );
8208     else
8209       newFaces.push_back( myLastCreatedElems[i] );
8210   }
8211   // get segments adjacent to merged nodes
8212   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8213   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8214   {
8215     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8216     if ( nodes.front()->IsNull() ) continue;
8217     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8218     while ( segIt->more() )
8219       segments.insert( segIt->next() );
8220   }
8221
8222   // find coincident
8223   TListOfListOfElementsID equalGroups;
8224   if ( !segments.empty() )
8225     FindEqualElements( segments, equalGroups );
8226   if ( !equalGroups.empty() )
8227   {
8228     // remove from segments those that will be removed
8229     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8230     for ( ; itGroups != equalGroups.end(); ++itGroups )
8231     {
8232       list< int >& group = *itGroups;
8233       list< int >::iterator id = group.begin();
8234       for ( ++id; id != group.end(); ++id )
8235         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8236           segments.erase( seg );
8237     }
8238     // remove equal segments
8239     MergeElements( equalGroups );
8240
8241     // restore myLastCreatedElems
8242     myLastCreatedElems = newFaces;
8243     TIDSortedElemSet::iterator seg = segments.begin();
8244     for ( ; seg != segments.end(); ++seg )
8245       myLastCreatedElems.push_back( *seg );
8246   }
8247
8248   return aResult;
8249 }
8250
8251 //=======================================================================
8252 //function : InsertNodesIntoLink
8253 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8254 //           and theBetweenNode2 and split theElement
8255 //=======================================================================
8256
8257 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8258                                            const SMDS_MeshNode*        theBetweenNode1,
8259                                            const SMDS_MeshNode*        theBetweenNode2,
8260                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8261                                            const bool                  toCreatePoly)
8262 {
8263   if ( !theElement ) return;
8264
8265   SMESHDS_Mesh *aMesh = GetMeshDS();
8266   vector<const SMDS_MeshElement*> newElems;
8267
8268   if ( theElement->GetType() == SMDSAbs_Edge )
8269   {
8270     theNodesToInsert.push_front( theBetweenNode1 );
8271     theNodesToInsert.push_back ( theBetweenNode2 );
8272     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8273     const SMDS_MeshNode* n1 = *n;
8274     for ( ++n; n != theNodesToInsert.end(); ++n )
8275     {
8276       const SMDS_MeshNode* n2 = *n;
8277       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8278         AddToSameGroups( seg, theElement, aMesh );
8279       else
8280         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8281       n1 = n2;
8282     }
8283     theNodesToInsert.pop_front();
8284     theNodesToInsert.pop_back();
8285
8286     if ( theElement->IsQuadratic() ) // add a not split part
8287     {
8288       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8289                                           theElement->end_nodes() );
8290       int iOther = 0, nbN = nodes.size();
8291       for ( ; iOther < nbN; ++iOther )
8292         if ( nodes[iOther] != theBetweenNode1 &&
8293              nodes[iOther] != theBetweenNode2 )
8294           break;
8295       if      ( iOther == 0 )
8296       {
8297         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8298           AddToSameGroups( seg, theElement, aMesh );
8299         else
8300           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8301       }
8302       else if ( iOther == 2 )
8303       {
8304         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8305           AddToSameGroups( seg, theElement, aMesh );
8306         else
8307           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8308       }
8309     }
8310     // treat new elements
8311     for ( size_t i = 0; i < newElems.size(); ++i )
8312       if ( newElems[i] )
8313       {
8314         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8315         myLastCreatedElems.push_back( newElems[i] );
8316       }
8317     ReplaceElemInGroups( theElement, newElems, aMesh );
8318     aMesh->RemoveElement( theElement );
8319     return;
8320
8321   } // if ( theElement->GetType() == SMDSAbs_Edge )
8322
8323   const SMDS_MeshElement* theFace = theElement;
8324   if ( theFace->GetType() != SMDSAbs_Face ) return;
8325
8326   // find indices of 2 link nodes and of the rest nodes
8327   int iNode = 0, il1, il2, i3, i4;
8328   il1 = il2 = i3 = i4 = -1;
8329   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8330
8331   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8332   while ( nodeIt->more() ) {
8333     const SMDS_MeshNode* n = nodeIt->next();
8334     if ( n == theBetweenNode1 )
8335       il1 = iNode;
8336     else if ( n == theBetweenNode2 )
8337       il2 = iNode;
8338     else if ( i3 < 0 )
8339       i3 = iNode;
8340     else
8341       i4 = iNode;
8342     nodes[ iNode++ ] = n;
8343   }
8344   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8345     return ;
8346
8347   // arrange link nodes to go one after another regarding the face orientation
8348   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8349   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8350   if ( reverse ) {
8351     iNode = il1;
8352     il1 = il2;
8353     il2 = iNode;
8354     aNodesToInsert.reverse();
8355   }
8356   // check that not link nodes of a quadrangles are in good order
8357   int nbFaceNodes = theFace->NbNodes();
8358   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8359     iNode = i3;
8360     i3 = i4;
8361     i4 = iNode;
8362   }
8363
8364   if (toCreatePoly || theFace->IsPoly()) {
8365
8366     iNode = 0;
8367     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8368
8369     // add nodes of face up to first node of link
8370     bool isFLN = false;
8371     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8372     while ( nodeIt->more() && !isFLN ) {
8373       const SMDS_MeshNode* n = nodeIt->next();
8374       poly_nodes[iNode++] = n;
8375       isFLN = ( n == nodes[il1] );
8376     }
8377     // add nodes to insert
8378     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8379     for (; nIt != aNodesToInsert.end(); nIt++) {
8380       poly_nodes[iNode++] = *nIt;
8381     }
8382     // add nodes of face starting from last node of link
8383     while ( nodeIt->more() ) {
8384       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8385       poly_nodes[iNode++] = n;
8386     }
8387
8388     // make a new face
8389     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8390   }
8391
8392   else if ( !theFace->IsQuadratic() )
8393   {
8394     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8395     int nbLinkNodes = 2 + aNodesToInsert.size();
8396     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8397     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8398     linkNodes[ 0 ] = nodes[ il1 ];
8399     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8400     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8401     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8402       linkNodes[ iNode++ ] = *nIt;
8403     }
8404     // decide how to split a quadrangle: compare possible variants
8405     // and choose which of splits to be a quadrangle
8406     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8407     if ( nbFaceNodes == 3 ) {
8408       iBestQuad = nbSplits;
8409       i4 = i3;
8410     }
8411     else if ( nbFaceNodes == 4 ) {
8412       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8413       double aBestRate = DBL_MAX;
8414       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8415         i1 = 0; i2 = 1;
8416         double aBadRate = 0;
8417         // evaluate elements quality
8418         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8419           if ( iSplit == iQuad ) {
8420             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8421                                    linkNodes[ i2++ ],
8422                                    nodes[ i3 ],
8423                                    nodes[ i4 ]);
8424             aBadRate += getBadRate( &quad, aCrit );
8425           }
8426           else {
8427             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8428                                    linkNodes[ i2++ ],
8429                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8430             aBadRate += getBadRate( &tria, aCrit );
8431           }
8432         }
8433         // choice
8434         if ( aBadRate < aBestRate ) {
8435           iBestQuad = iQuad;
8436           aBestRate = aBadRate;
8437         }
8438       }
8439     }
8440
8441     // create new elements
8442     i1 = 0; i2 = 1;
8443     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8444     {
8445       if ( iSplit == iBestQuad )
8446         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8447                                             linkNodes[ i2++ ],
8448                                             nodes[ i3 ],
8449                                             nodes[ i4 ]));
8450       else
8451         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8452                                             linkNodes[ i2++ ],
8453                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8454     }
8455
8456     const SMDS_MeshNode* newNodes[ 4 ];
8457     newNodes[ 0 ] = linkNodes[ i1 ];
8458     newNodes[ 1 ] = linkNodes[ i2 ];
8459     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8460     newNodes[ 3 ] = nodes[ i4 ];
8461     if (iSplit == iBestQuad)
8462       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8463     else
8464       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8465
8466   } // end if(!theFace->IsQuadratic())
8467
8468   else { // theFace is quadratic
8469     // we have to split theFace on simple triangles and one simple quadrangle
8470     int tmp = il1/2;
8471     int nbshift = tmp*2;
8472     // shift nodes in nodes[] by nbshift
8473     int i,j;
8474     for(i=0; i<nbshift; i++) {
8475       const SMDS_MeshNode* n = nodes[0];
8476       for(j=0; j<nbFaceNodes-1; j++) {
8477         nodes[j] = nodes[j+1];
8478       }
8479       nodes[nbFaceNodes-1] = n;
8480     }
8481     il1 = il1 - nbshift;
8482     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8483     //   n0      n1     n2    n0      n1     n2
8484     //     +-----+-----+        +-----+-----+
8485     //      \         /         |           |
8486     //       \       /          |           |
8487     //      n5+     +n3       n7+           +n3
8488     //         \   /            |           |
8489     //          \ /             |           |
8490     //           +              +-----+-----+
8491     //           n4           n6      n5     n4
8492
8493     // create new elements
8494     int n1,n2,n3;
8495     if ( nbFaceNodes == 6 ) { // quadratic triangle
8496       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8497       if ( theFace->IsMediumNode(nodes[il1]) ) {
8498         // create quadrangle
8499         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8500         n1 = 1;
8501         n2 = 2;
8502         n3 = 3;
8503       }
8504       else {
8505         // create quadrangle
8506         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8507         n1 = 0;
8508         n2 = 1;
8509         n3 = 5;
8510       }
8511     }
8512     else { // nbFaceNodes==8 - quadratic quadrangle
8513       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8514       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8515       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8516       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8517         // create quadrangle
8518         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8519         n1 = 1;
8520         n2 = 2;
8521         n3 = 3;
8522       }
8523       else {
8524         // create quadrangle
8525         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8526         n1 = 0;
8527         n2 = 1;
8528         n3 = 7;
8529       }
8530     }
8531     // create needed triangles using n1,n2,n3 and inserted nodes
8532     int nbn = 2 + aNodesToInsert.size();
8533     vector<const SMDS_MeshNode*> aNodes(nbn);
8534     aNodes[0    ] = nodes[n1];
8535     aNodes[nbn-1] = nodes[n2];
8536     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8537     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8538       aNodes[iNode++] = *nIt;
8539     }
8540     for ( i = 1; i < nbn; i++ )
8541       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8542   }
8543
8544   // remove the old face
8545   for ( size_t i = 0; i < newElems.size(); ++i )
8546     if ( newElems[i] )
8547     {
8548       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8549       myLastCreatedElems.push_back( newElems[i] );
8550     }
8551   ReplaceElemInGroups( theFace, newElems, aMesh );
8552   aMesh->RemoveElement(theFace);
8553
8554 } // InsertNodesIntoLink()
8555
8556 //=======================================================================
8557 //function : UpdateVolumes
8558 //purpose  :
8559 //=======================================================================
8560
8561 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8562                                       const SMDS_MeshNode*        theBetweenNode2,
8563                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8564 {
8565   ClearLastCreated();
8566
8567   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8568   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8569     const SMDS_MeshElement* elem = invElemIt->next();
8570
8571     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8572     SMDS_VolumeTool aVolume (elem);
8573     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8574       continue;
8575
8576     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8577     int iface, nbFaces = aVolume.NbFaces();
8578     vector<const SMDS_MeshNode *> poly_nodes;
8579     vector<int> quantities (nbFaces);
8580
8581     for (iface = 0; iface < nbFaces; iface++) {
8582       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8583       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8584       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8585
8586       for (int inode = 0; inode < nbFaceNodes; inode++) {
8587         poly_nodes.push_back(faceNodes[inode]);
8588
8589         if (nbInserted == 0) {
8590           if (faceNodes[inode] == theBetweenNode1) {
8591             if (faceNodes[inode + 1] == theBetweenNode2) {
8592               nbInserted = theNodesToInsert.size();
8593
8594               // add nodes to insert
8595               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8596               for (; nIt != theNodesToInsert.end(); nIt++) {
8597                 poly_nodes.push_back(*nIt);
8598               }
8599             }
8600           }
8601           else if (faceNodes[inode] == theBetweenNode2) {
8602             if (faceNodes[inode + 1] == theBetweenNode1) {
8603               nbInserted = theNodesToInsert.size();
8604
8605               // add nodes to insert in reversed order
8606               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8607               nIt--;
8608               for (; nIt != theNodesToInsert.begin(); nIt--) {
8609                 poly_nodes.push_back(*nIt);
8610               }
8611               poly_nodes.push_back(*nIt);
8612             }
8613           }
8614           else {
8615           }
8616         }
8617       }
8618       quantities[iface] = nbFaceNodes + nbInserted;
8619     }
8620
8621     // Replace the volume
8622     SMESHDS_Mesh *aMesh = GetMeshDS();
8623
8624     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8625     {
8626       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8627       myLastCreatedElems.push_back( newElem );
8628       ReplaceElemInGroups( elem, newElem, aMesh );
8629     }
8630     aMesh->RemoveElement( elem );
8631   }
8632 }
8633
8634 namespace
8635 {
8636   //================================================================================
8637   /*!
8638    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8639    */
8640   //================================================================================
8641
8642   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8643                            vector<const SMDS_MeshNode *> & nodes,
8644                            vector<int> &                   nbNodeInFaces )
8645   {
8646     nodes.clear();
8647     nbNodeInFaces.clear();
8648     SMDS_VolumeTool vTool ( elem );
8649     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8650     {
8651       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8652       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8653       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8654     }
8655   }
8656 }
8657
8658 //=======================================================================
8659 /*!
8660  * \brief Convert elements contained in a sub-mesh to quadratic
8661  * \return int - nb of checked elements
8662  */
8663 //=======================================================================
8664
8665 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8666                                              SMESH_MesherHelper& theHelper,
8667                                              const bool          theForce3d)
8668 {
8669   //MESSAGE("convertElemToQuadratic");
8670   int nbElem = 0;
8671   if( !theSm ) return nbElem;
8672
8673   vector<int> nbNodeInFaces;
8674   vector<const SMDS_MeshNode *> nodes;
8675   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8676   while(ElemItr->more())
8677   {
8678     nbElem++;
8679     const SMDS_MeshElement* elem = ElemItr->next();
8680     if( !elem ) continue;
8681
8682     // analyse a necessity of conversion
8683     const SMDSAbs_ElementType aType = elem->GetType();
8684     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8685       continue;
8686     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8687     bool hasCentralNodes = false;
8688     if ( elem->IsQuadratic() )
8689     {
8690       bool alreadyOK;
8691       switch ( aGeomType ) {
8692       case SMDSEntity_Quad_Triangle:
8693       case SMDSEntity_Quad_Quadrangle:
8694       case SMDSEntity_Quad_Hexa:
8695       case SMDSEntity_Quad_Penta:
8696         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8697
8698       case SMDSEntity_BiQuad_Triangle:
8699       case SMDSEntity_BiQuad_Quadrangle:
8700       case SMDSEntity_TriQuad_Hexa:
8701       case SMDSEntity_BiQuad_Penta:
8702         alreadyOK = theHelper.GetIsBiQuadratic();
8703         hasCentralNodes = true;
8704         break;
8705       default:
8706         alreadyOK = true;
8707       }
8708       // take into account already present medium nodes
8709       switch ( aType ) {
8710       case SMDSAbs_Volume:
8711         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8712       case SMDSAbs_Face:
8713         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8714       case SMDSAbs_Edge:
8715         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8716       default:;
8717       }
8718       if ( alreadyOK )
8719         continue;
8720     }
8721     // get elem data needed to re-create it
8722     //
8723     const int id      = elem->GetID();
8724     const int nbNodes = elem->NbCornerNodes();
8725     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8726     if ( aGeomType == SMDSEntity_Polyhedra )
8727       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8728     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8729       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8730
8731     // remove a linear element
8732     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8733
8734     // remove central nodes of biquadratic elements (biquad->quad conversion)
8735     if ( hasCentralNodes )
8736       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8737         if ( nodes[i]->NbInverseElements() == 0 )
8738           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8739
8740     const SMDS_MeshElement* NewElem = 0;
8741
8742     switch( aType )
8743     {
8744     case SMDSAbs_Edge :
8745     {
8746       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8747       break;
8748     }
8749     case SMDSAbs_Face :
8750     {
8751       switch(nbNodes)
8752       {
8753       case 3:
8754         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8755         break;
8756       case 4:
8757         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8758         break;
8759       default:
8760         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8761       }
8762       break;
8763     }
8764     case SMDSAbs_Volume :
8765     {
8766       switch( aGeomType )
8767       {
8768       case SMDSEntity_Tetra:
8769         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8770         break;
8771       case SMDSEntity_Pyramid:
8772         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8773         break;
8774       case SMDSEntity_Penta:
8775       case SMDSEntity_Quad_Penta:
8776       case SMDSEntity_BiQuad_Penta:
8777         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8778         break;
8779       case SMDSEntity_Hexa:
8780       case SMDSEntity_Quad_Hexa:
8781       case SMDSEntity_TriQuad_Hexa:
8782         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8783                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8784         break;
8785       case SMDSEntity_Hexagonal_Prism:
8786       default:
8787         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8788       }
8789       break;
8790     }
8791     default :
8792       continue;
8793     }
8794     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8795     if( NewElem && NewElem->getshapeId() < 1 )
8796       theSm->AddElement( NewElem );
8797   }
8798   return nbElem;
8799 }
8800 //=======================================================================
8801 //function : ConvertToQuadratic
8802 //purpose  :
8803 //=======================================================================
8804
8805 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8806 {
8807   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8808   SMESHDS_Mesh* meshDS = GetMeshDS();
8809
8810   SMESH_MesherHelper aHelper(*myMesh);
8811
8812   aHelper.SetIsQuadratic( true );
8813   aHelper.SetIsBiQuadratic( theToBiQuad );
8814   aHelper.SetElementsOnShape(true);
8815   aHelper.ToFixNodeParameters( true );
8816
8817   // convert elements assigned to sub-meshes
8818   int nbCheckedElems = 0;
8819   if ( myMesh->HasShapeToMesh() )
8820   {
8821     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8822     {
8823       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8824       while ( smIt->more() ) {
8825         SMESH_subMesh* sm = smIt->next();
8826         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8827           aHelper.SetSubShape( sm->GetSubShape() );
8828           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8829         }
8830       }
8831     }
8832   }
8833
8834   // convert elements NOT assigned to sub-meshes
8835   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8836   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8837   {
8838     aHelper.SetElementsOnShape(false);
8839     SMESHDS_SubMesh *smDS = 0;
8840
8841     // convert edges
8842     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8843     while( aEdgeItr->more() )
8844     {
8845       const SMDS_MeshEdge* edge = aEdgeItr->next();
8846       if ( !edge->IsQuadratic() )
8847       {
8848         int                  id = edge->GetID();
8849         const SMDS_MeshNode* n1 = edge->GetNode(0);
8850         const SMDS_MeshNode* n2 = edge->GetNode(1);
8851
8852         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8853
8854         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8855         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8856       }
8857       else
8858       {
8859         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8860       }
8861     }
8862
8863     // convert faces
8864     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8865     while( aFaceItr->more() )
8866     {
8867       const SMDS_MeshFace* face = aFaceItr->next();
8868       if ( !face ) continue;
8869       
8870       const SMDSAbs_EntityType type = face->GetEntityType();
8871       bool alreadyOK;
8872       switch( type )
8873       {
8874       case SMDSEntity_Quad_Triangle:
8875       case SMDSEntity_Quad_Quadrangle:
8876         alreadyOK = !theToBiQuad;
8877         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8878         break;
8879       case SMDSEntity_BiQuad_Triangle:
8880       case SMDSEntity_BiQuad_Quadrangle:
8881         alreadyOK = theToBiQuad;
8882         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8883         break;
8884       default: alreadyOK = false;
8885       }
8886       if ( alreadyOK )
8887         continue;
8888
8889       const int id = face->GetID();
8890       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8891
8892       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8893
8894       SMDS_MeshFace * NewFace = 0;
8895       switch( type )
8896       {
8897       case SMDSEntity_Triangle:
8898       case SMDSEntity_Quad_Triangle:
8899       case SMDSEntity_BiQuad_Triangle:
8900         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8901         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8902           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8903         break;
8904
8905       case SMDSEntity_Quadrangle:
8906       case SMDSEntity_Quad_Quadrangle:
8907       case SMDSEntity_BiQuad_Quadrangle:
8908         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8909         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8910           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8911         break;
8912
8913       default:;
8914         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8915       }
8916       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8917     }
8918
8919     // convert volumes
8920     vector<int> nbNodeInFaces;
8921     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8922     while(aVolumeItr->more())
8923     {
8924       const SMDS_MeshVolume* volume = aVolumeItr->next();
8925       if ( !volume ) continue;
8926
8927       const SMDSAbs_EntityType type = volume->GetEntityType();
8928       if ( volume->IsQuadratic() )
8929       {
8930         bool alreadyOK;
8931         switch ( type )
8932         {
8933         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8934         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8935         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8936         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8937         default:                      alreadyOK = true;
8938         }
8939         if ( alreadyOK )
8940         {
8941           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8942           continue;
8943         }
8944       }
8945       const int id = volume->GetID();
8946       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8947       if ( type == SMDSEntity_Polyhedra )
8948         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8949       else if ( type == SMDSEntity_Hexagonal_Prism )
8950         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8951
8952       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8953
8954       SMDS_MeshVolume * NewVolume = 0;
8955       switch ( type )
8956       {
8957       case SMDSEntity_Tetra:
8958         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8959         break;
8960       case SMDSEntity_Hexa:
8961       case SMDSEntity_Quad_Hexa:
8962       case SMDSEntity_TriQuad_Hexa:
8963         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8964                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8965         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8966           if ( nodes[i]->NbInverseElements() == 0 )
8967             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8968         break;
8969       case SMDSEntity_Pyramid:
8970         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8971                                       nodes[3], nodes[4], id, theForce3d);
8972         break;
8973       case SMDSEntity_Penta:
8974       case SMDSEntity_Quad_Penta:
8975       case SMDSEntity_BiQuad_Penta:
8976         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8977                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8978         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8979           if ( nodes[i]->NbInverseElements() == 0 )
8980             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8981         break;
8982       case SMDSEntity_Hexagonal_Prism:
8983       default:
8984         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8985       }
8986       ReplaceElemInGroups(volume, NewVolume, meshDS);
8987     }
8988   }
8989
8990   if ( !theForce3d )
8991   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8992     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8993     // aHelper.FixQuadraticElements(myError);
8994     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8995   }
8996 }
8997
8998 //================================================================================
8999 /*!
9000  * \brief Makes given elements quadratic
9001  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9002  *  \param theElements - elements to make quadratic
9003  */
9004 //================================================================================
9005
9006 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9007                                           TIDSortedElemSet& theElements,
9008                                           const bool        theToBiQuad)
9009 {
9010   if ( theElements.empty() ) return;
9011
9012   // we believe that all theElements are of the same type
9013   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9014
9015   // get all nodes shared by theElements
9016   TIDSortedNodeSet allNodes;
9017   TIDSortedElemSet::iterator eIt = theElements.begin();
9018   for ( ; eIt != theElements.end(); ++eIt )
9019     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9020
9021   // complete theElements with elements of lower dim whose all nodes are in allNodes
9022
9023   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9024   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9025   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9026   for ( ; nIt != allNodes.end(); ++nIt )
9027   {
9028     const SMDS_MeshNode* n = *nIt;
9029     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9030     while ( invIt->more() )
9031     {
9032       const SMDS_MeshElement*      e = invIt->next();
9033       const SMDSAbs_ElementType type = e->GetType();
9034       if ( e->IsQuadratic() )
9035       {
9036         quadAdjacentElems[ type ].insert( e );
9037
9038         bool alreadyOK;
9039         switch ( e->GetEntityType() ) {
9040         case SMDSEntity_Quad_Triangle:
9041         case SMDSEntity_Quad_Quadrangle:
9042         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9043         case SMDSEntity_BiQuad_Triangle:
9044         case SMDSEntity_BiQuad_Quadrangle:
9045         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9046         default:                           alreadyOK = true;
9047         }
9048         if ( alreadyOK )
9049           continue;
9050       }
9051       if ( type >= elemType )
9052         continue; // same type or more complex linear element
9053
9054       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9055         continue; // e is already checked
9056
9057       // check nodes
9058       bool allIn = true;
9059       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9060       while ( nodeIt->more() && allIn )
9061         allIn = allNodes.count( nodeIt->next() );
9062       if ( allIn )
9063         theElements.insert(e );
9064     }
9065   }
9066
9067   SMESH_MesherHelper helper(*myMesh);
9068   helper.SetIsQuadratic( true );
9069   helper.SetIsBiQuadratic( theToBiQuad );
9070
9071   // add links of quadratic adjacent elements to the helper
9072
9073   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9074     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9075           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9076     {
9077       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9078     }
9079   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9080     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9081           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9082     {
9083       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9084     }
9085   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9086     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9087           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9088     {
9089       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9090     }
9091
9092   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9093
9094   SMESHDS_Mesh*  meshDS = GetMeshDS();
9095   SMESHDS_SubMesh* smDS = 0;
9096   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9097   {
9098     const SMDS_MeshElement* elem = *eIt;
9099
9100     bool alreadyOK;
9101     int nbCentralNodes = 0;
9102     switch ( elem->GetEntityType() ) {
9103       // linear convertible
9104     case SMDSEntity_Edge:
9105     case SMDSEntity_Triangle:
9106     case SMDSEntity_Quadrangle:
9107     case SMDSEntity_Tetra:
9108     case SMDSEntity_Pyramid:
9109     case SMDSEntity_Hexa:
9110     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9111       // quadratic that can become bi-quadratic
9112     case SMDSEntity_Quad_Triangle:
9113     case SMDSEntity_Quad_Quadrangle:
9114     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9115       // bi-quadratic
9116     case SMDSEntity_BiQuad_Triangle:
9117     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9118     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9119       // the rest
9120     default:                           alreadyOK = true;
9121     }
9122     if ( alreadyOK ) continue;
9123
9124     const SMDSAbs_ElementType type = elem->GetType();
9125     const int                   id = elem->GetID();
9126     const int              nbNodes = elem->NbCornerNodes();
9127     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9128
9129     helper.SetSubShape( elem->getshapeId() );
9130
9131     if ( !smDS || !smDS->Contains( elem ))
9132       smDS = meshDS->MeshElements( elem->getshapeId() );
9133     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9134
9135     SMDS_MeshElement * newElem = 0;
9136     switch( nbNodes )
9137     {
9138     case 4: // cases for most frequently used element types go first (for optimization)
9139       if ( type == SMDSAbs_Volume )
9140         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9141       else
9142         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9143       break;
9144     case 8:
9145       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9146                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9147       break;
9148     case 3:
9149       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9150       break;
9151     case 2:
9152       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9153       break;
9154     case 5:
9155       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9156                                  nodes[4], id, theForce3d);
9157       break;
9158     case 6:
9159       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9160                                  nodes[4], nodes[5], id, theForce3d);
9161       break;
9162     default:;
9163     }
9164     ReplaceElemInGroups( elem, newElem, meshDS);
9165     if( newElem && smDS )
9166       smDS->AddElement( newElem );
9167
9168     // remove central nodes
9169     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9170       if ( nodes[i]->NbInverseElements() == 0 )
9171         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9172
9173   } // loop on theElements
9174
9175   if ( !theForce3d )
9176   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9177     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9178     // helper.FixQuadraticElements( myError );
9179     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9180   }
9181 }
9182
9183 //=======================================================================
9184 /*!
9185  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9186  * \return int - nb of checked elements
9187  */
9188 //=======================================================================
9189
9190 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9191                                      SMDS_ElemIteratorPtr theItr,
9192                                      const int            theShapeID)
9193 {
9194   int nbElem = 0;
9195   SMESHDS_Mesh* meshDS = GetMeshDS();
9196   ElemFeatures elemType;
9197   vector<const SMDS_MeshNode *> nodes;
9198
9199   while( theItr->more() )
9200   {
9201     const SMDS_MeshElement* elem = theItr->next();
9202     nbElem++;
9203     if( elem && elem->IsQuadratic())
9204     {
9205       // get elem data
9206       int nbCornerNodes = elem->NbCornerNodes();
9207       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9208
9209       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9210
9211       //remove a quadratic element
9212       if ( !theSm || !theSm->Contains( elem ))
9213         theSm = meshDS->MeshElements( elem->getshapeId() );
9214       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9215
9216       // remove medium nodes
9217       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9218         if ( nodes[i]->NbInverseElements() == 0 )
9219           meshDS->RemoveFreeNode( nodes[i], theSm );
9220
9221       // add a linear element
9222       nodes.resize( nbCornerNodes );
9223       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9224       ReplaceElemInGroups(elem, newElem, meshDS);
9225       if( theSm && newElem )
9226         theSm->AddElement( newElem );
9227     }
9228   }
9229   return nbElem;
9230 }
9231
9232 //=======================================================================
9233 //function : ConvertFromQuadratic
9234 //purpose  :
9235 //=======================================================================
9236
9237 bool SMESH_MeshEditor::ConvertFromQuadratic()
9238 {
9239   int nbCheckedElems = 0;
9240   if ( myMesh->HasShapeToMesh() )
9241   {
9242     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9243     {
9244       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9245       while ( smIt->more() ) {
9246         SMESH_subMesh* sm = smIt->next();
9247         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9248           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9249       }
9250     }
9251   }
9252
9253   int totalNbElems =
9254     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9255   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9256   {
9257     SMESHDS_SubMesh *aSM = 0;
9258     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9259   }
9260
9261   return true;
9262 }
9263
9264 namespace
9265 {
9266   //================================================================================
9267   /*!
9268    * \brief Return true if all medium nodes of the element are in the node set
9269    */
9270   //================================================================================
9271
9272   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9273   {
9274     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9275       if ( !nodeSet.count( elem->GetNode(i) ))
9276         return false;
9277     return true;
9278   }
9279 }
9280
9281 //================================================================================
9282 /*!
9283  * \brief Makes given elements linear
9284  */
9285 //================================================================================
9286
9287 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9288 {
9289   if ( theElements.empty() ) return;
9290
9291   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9292   set<int> mediumNodeIDs;
9293   TIDSortedElemSet::iterator eIt = theElements.begin();
9294   for ( ; eIt != theElements.end(); ++eIt )
9295   {
9296     const SMDS_MeshElement* e = *eIt;
9297     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9298       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9299   }
9300
9301   // replace given elements by linear ones
9302   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9303   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9304
9305   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9306   // except those elements sharing medium nodes of quadratic element whose medium nodes
9307   // are not all in mediumNodeIDs
9308
9309   // get remaining medium nodes
9310   TIDSortedNodeSet mediumNodes;
9311   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9312   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9313     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9314       mediumNodes.insert( mediumNodes.end(), n );
9315
9316   // find more quadratic elements to convert
9317   TIDSortedElemSet moreElemsToConvert;
9318   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9319   for ( ; nIt != mediumNodes.end(); ++nIt )
9320   {
9321     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9322     while ( invIt->more() )
9323     {
9324       const SMDS_MeshElement* e = invIt->next();
9325       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9326       {
9327         // find a more complex element including e and
9328         // whose medium nodes are not in mediumNodes
9329         bool complexFound = false;
9330         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9331         {
9332           SMDS_ElemIteratorPtr invIt2 =
9333             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9334           while ( invIt2->more() )
9335           {
9336             const SMDS_MeshElement* eComplex = invIt2->next();
9337             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9338             {
9339               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9340               if ( nbCommonNodes == e->NbNodes())
9341               {
9342                 complexFound = true;
9343                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9344                 break;
9345               }
9346             }
9347           }
9348         }
9349         if ( !complexFound )
9350           moreElemsToConvert.insert( e );
9351       }
9352     }
9353   }
9354   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9355   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9356 }
9357
9358 //=======================================================================
9359 //function : SewSideElements
9360 //purpose  :
9361 //=======================================================================
9362
9363 SMESH_MeshEditor::Sew_Error
9364 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9365                                    TIDSortedElemSet&    theSide2,
9366                                    const SMDS_MeshNode* theFirstNode1,
9367                                    const SMDS_MeshNode* theFirstNode2,
9368                                    const SMDS_MeshNode* theSecondNode1,
9369                                    const SMDS_MeshNode* theSecondNode2)
9370 {
9371   ClearLastCreated();
9372
9373   if ( theSide1.size() != theSide2.size() )
9374     return SEW_DIFF_NB_OF_ELEMENTS;
9375
9376   Sew_Error aResult = SEW_OK;
9377   // Algo:
9378   // 1. Build set of faces representing each side
9379   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9380   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9381
9382   // =======================================================================
9383   // 1. Build set of faces representing each side:
9384   // =======================================================================
9385   // a. build set of nodes belonging to faces
9386   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9387   // c. create temporary faces representing side of volumes if correspondent
9388   //    face does not exist
9389
9390   SMESHDS_Mesh* aMesh = GetMeshDS();
9391   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9392   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9393   TIDSortedElemSet             faceSet1, faceSet2;
9394   set<const SMDS_MeshElement*> volSet1,  volSet2;
9395   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9396   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9397   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9398   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9399   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9400   int iSide, iFace, iNode;
9401
9402   list<const SMDS_MeshElement* > tempFaceList;
9403   for ( iSide = 0; iSide < 2; iSide++ ) {
9404     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9405     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9406     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9407     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9408     set<const SMDS_MeshElement*>::iterator vIt;
9409     TIDSortedElemSet::iterator eIt;
9410     set<const SMDS_MeshNode*>::iterator    nIt;
9411
9412     // check that given nodes belong to given elements
9413     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9414     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9415     int firstIndex = -1, secondIndex = -1;
9416     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9417       const SMDS_MeshElement* elem = *eIt;
9418       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9419       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9420       if ( firstIndex > -1 && secondIndex > -1 ) break;
9421     }
9422     if ( firstIndex < 0 || secondIndex < 0 ) {
9423       // we can simply return until temporary faces created
9424       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9425     }
9426
9427     // -----------------------------------------------------------
9428     // 1a. Collect nodes of existing faces
9429     //     and build set of face nodes in order to detect missing
9430     //     faces corresponding to sides of volumes
9431     // -----------------------------------------------------------
9432
9433     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9434
9435     // loop on the given element of a side
9436     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9437       //const SMDS_MeshElement* elem = *eIt;
9438       const SMDS_MeshElement* elem = *eIt;
9439       if ( elem->GetType() == SMDSAbs_Face ) {
9440         faceSet->insert( elem );
9441         set <const SMDS_MeshNode*> faceNodeSet;
9442         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9443         while ( nodeIt->more() ) {
9444           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9445           nodeSet->insert( n );
9446           faceNodeSet.insert( n );
9447         }
9448         setOfFaceNodeSet.insert( faceNodeSet );
9449       }
9450       else if ( elem->GetType() == SMDSAbs_Volume )
9451         volSet->insert( elem );
9452     }
9453     // ------------------------------------------------------------------------------
9454     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9455     // ------------------------------------------------------------------------------
9456
9457     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9458       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9459       while ( fIt->more() ) { // loop on faces sharing a node
9460         const SMDS_MeshElement* f = fIt->next();
9461         if ( faceSet->find( f ) == faceSet->end() ) {
9462           // check if all nodes are in nodeSet and
9463           // complete setOfFaceNodeSet if they are
9464           set <const SMDS_MeshNode*> faceNodeSet;
9465           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9466           bool allInSet = true;
9467           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9468             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9469             if ( nodeSet->find( n ) == nodeSet->end() )
9470               allInSet = false;
9471             else
9472               faceNodeSet.insert( n );
9473           }
9474           if ( allInSet ) {
9475             faceSet->insert( f );
9476             setOfFaceNodeSet.insert( faceNodeSet );
9477           }
9478         }
9479       }
9480     }
9481
9482     // -------------------------------------------------------------------------
9483     // 1c. Create temporary faces representing sides of volumes if correspondent
9484     //     face does not exist
9485     // -------------------------------------------------------------------------
9486
9487     if ( !volSet->empty() ) {
9488       //int nodeSetSize = nodeSet->size();
9489
9490       // loop on given volumes
9491       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9492         SMDS_VolumeTool vol (*vIt);
9493         // loop on volume faces: find free faces
9494         // --------------------------------------
9495         list<const SMDS_MeshElement* > freeFaceList;
9496         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9497           if ( !vol.IsFreeFace( iFace ))
9498             continue;
9499           // check if there is already a face with same nodes in a face set
9500           const SMDS_MeshElement* aFreeFace = 0;
9501           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9502           int nbNodes = vol.NbFaceNodes( iFace );
9503           set <const SMDS_MeshNode*> faceNodeSet;
9504           vol.GetFaceNodes( iFace, faceNodeSet );
9505           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9506           if ( isNewFace ) {
9507             // no such a face is given but it still can exist, check it
9508             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9509             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9510           }
9511           if ( !aFreeFace ) {
9512             // create a temporary face
9513             if ( nbNodes == 3 ) {
9514               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9515               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9516             }
9517             else if ( nbNodes == 4 ) {
9518               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9519               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9520             }
9521             else {
9522               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9523               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9524               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9525             }
9526             if ( aFreeFace )
9527               tempFaceList.push_back( aFreeFace );
9528           }
9529
9530           if ( aFreeFace )
9531             freeFaceList.push_back( aFreeFace );
9532
9533         } // loop on faces of a volume
9534
9535         // choose one of several free faces of a volume
9536         // --------------------------------------------
9537         if ( freeFaceList.size() > 1 ) {
9538           // choose a face having max nb of nodes shared by other elems of a side
9539           int maxNbNodes = -1;
9540           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9541           while ( fIt != freeFaceList.end() ) { // loop on free faces
9542             int nbSharedNodes = 0;
9543             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9544             while ( nodeIt->more() ) { // loop on free face nodes
9545               const SMDS_MeshNode* n =
9546                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9547               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9548               while ( invElemIt->more() ) {
9549                 const SMDS_MeshElement* e = invElemIt->next();
9550                 nbSharedNodes += faceSet->count( e );
9551                 nbSharedNodes += elemSet->count( e );
9552               }
9553             }
9554             if ( nbSharedNodes > maxNbNodes ) {
9555               maxNbNodes = nbSharedNodes;
9556               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9557             }
9558             else if ( nbSharedNodes == maxNbNodes ) {
9559               fIt++;
9560             }
9561             else {
9562               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9563             }
9564           }
9565           if ( freeFaceList.size() > 1 )
9566           {
9567             // could not choose one face, use another way
9568             // choose a face most close to the bary center of the opposite side
9569             gp_XYZ aBC( 0., 0., 0. );
9570             set <const SMDS_MeshNode*> addedNodes;
9571             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9572             eIt = elemSet2->begin();
9573             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9574               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9575               while ( nodeIt->more() ) { // loop on free face nodes
9576                 const SMDS_MeshNode* n =
9577                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9578                 if ( addedNodes.insert( n ).second )
9579                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9580               }
9581             }
9582             aBC /= addedNodes.size();
9583             double minDist = DBL_MAX;
9584             fIt = freeFaceList.begin();
9585             while ( fIt != freeFaceList.end() ) { // loop on free faces
9586               double dist = 0;
9587               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9588               while ( nodeIt->more() ) { // loop on free face nodes
9589                 const SMDS_MeshNode* n =
9590                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9592                 dist += ( aBC - p ).SquareModulus();
9593               }
9594               if ( dist < minDist ) {
9595                 minDist = dist;
9596                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9597               }
9598               else
9599                 fIt = freeFaceList.erase( fIt++ );
9600             }
9601           }
9602         } // choose one of several free faces of a volume
9603
9604         if ( freeFaceList.size() == 1 ) {
9605           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9606           faceSet->insert( aFreeFace );
9607           // complete a node set with nodes of a found free face
9608           //           for ( iNode = 0; iNode < ; iNode++ )
9609           //             nodeSet->insert( fNodes[ iNode ] );
9610         }
9611
9612       } // loop on volumes of a side
9613
9614       //       // complete a set of faces if new nodes in a nodeSet appeared
9615       //       // ----------------------------------------------------------
9616       //       if ( nodeSetSize != nodeSet->size() ) {
9617       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9618       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9619       //           while ( fIt->more() ) { // loop on faces sharing a node
9620       //             const SMDS_MeshElement* f = fIt->next();
9621       //             if ( faceSet->find( f ) == faceSet->end() ) {
9622       //               // check if all nodes are in nodeSet and
9623       //               // complete setOfFaceNodeSet if they are
9624       //               set <const SMDS_MeshNode*> faceNodeSet;
9625       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9626       //               bool allInSet = true;
9627       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9628       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9629       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9630       //                   allInSet = false;
9631       //                 else
9632       //                   faceNodeSet.insert( n );
9633       //               }
9634       //               if ( allInSet ) {
9635       //                 faceSet->insert( f );
9636       //                 setOfFaceNodeSet.insert( faceNodeSet );
9637       //               }
9638       //             }
9639       //           }
9640       //         }
9641       //       }
9642     } // Create temporary faces, if there are volumes given
9643   } // loop on sides
9644
9645   if ( faceSet1.size() != faceSet2.size() ) {
9646     // delete temporary faces: they are in reverseElements of actual nodes
9647     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9648     //    while ( tmpFaceIt->more() )
9649     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9650     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9651     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9652     //      aMesh->RemoveElement(*tmpFaceIt);
9653     MESSAGE("Diff nb of faces");
9654     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9655   }
9656
9657   // ============================================================
9658   // 2. Find nodes to merge:
9659   //              bind a node to remove to a node to put instead
9660   // ============================================================
9661
9662   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9663   if ( theFirstNode1 != theFirstNode2 )
9664     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9665   if ( theSecondNode1 != theSecondNode2 )
9666     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9667
9668   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9669   set< long > linkIdSet; // links to process
9670   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9671
9672   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9673   list< NLink > linkList[2];
9674   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9675   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9676   // loop on links in linkList; find faces by links and append links
9677   // of the found faces to linkList
9678   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9679   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9680   {
9681     NLink link[] = { *linkIt[0], *linkIt[1] };
9682     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9683     if ( !linkIdSet.count( linkID ) )
9684       continue;
9685
9686     // by links, find faces in the face sets,
9687     // and find indices of link nodes in the found faces;
9688     // in a face set, there is only one or no face sharing a link
9689     // ---------------------------------------------------------------
9690
9691     const SMDS_MeshElement* face[] = { 0, 0 };
9692     vector<const SMDS_MeshNode*> fnodes[2];
9693     int iLinkNode[2][2];
9694     TIDSortedElemSet avoidSet;
9695     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9696       const SMDS_MeshNode* n1 = link[iSide].first;
9697       const SMDS_MeshNode* n2 = link[iSide].second;
9698       //cout << "Side " << iSide << " ";
9699       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9700       // find a face by two link nodes
9701       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9702                                                       *faceSetPtr[ iSide ], avoidSet,
9703                                                       &iLinkNode[iSide][0],
9704                                                       &iLinkNode[iSide][1] );
9705       if ( face[ iSide ])
9706       {
9707         //cout << " F " << face[ iSide]->GetID() <<endl;
9708         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9709         // put face nodes to fnodes
9710         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9711         fnodes[ iSide ].assign( nIt, nEnd );
9712         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9713       }
9714     }
9715
9716     // check similarity of elements of the sides
9717     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9718       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9719       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9720         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9721       }
9722       else {
9723         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9724       }
9725       break; // do not return because it's necessary to remove tmp faces
9726     }
9727
9728     // set nodes to merge
9729     // -------------------
9730
9731     if ( face[0] && face[1] )  {
9732       const int nbNodes = face[0]->NbNodes();
9733       if ( nbNodes != face[1]->NbNodes() ) {
9734         MESSAGE("Diff nb of face nodes");
9735         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9736         break; // do not return because it s necessary to remove tmp faces
9737       }
9738       bool reverse[] = { false, false }; // order of nodes in the link
9739       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9740         // analyse link orientation in faces
9741         int i1 = iLinkNode[ iSide ][ 0 ];
9742         int i2 = iLinkNode[ iSide ][ 1 ];
9743         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9744       }
9745       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9746       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9747       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9748       {
9749         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9750                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9751       }
9752
9753       // add other links of the faces to linkList
9754       // -----------------------------------------
9755
9756       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9757         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9758         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9759         if ( !iter_isnew.second ) { // already in a set: no need to process
9760           linkIdSet.erase( iter_isnew.first );
9761         }
9762         else // new in set == encountered for the first time: add
9763         {
9764           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9765           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9766           linkList[0].push_back ( NLink( n1, n2 ));
9767           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9768         }
9769       }
9770     } // 2 faces found
9771
9772     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9773       break;
9774
9775   } // loop on link lists
9776
9777   if ( aResult == SEW_OK &&
9778        ( //linkIt[0] != linkList[0].end() ||
9779         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9780     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9781              " " << (faceSetPtr[1]->empty()));
9782     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9783   }
9784
9785   // ====================================================================
9786   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9787   // ====================================================================
9788
9789   // delete temporary faces
9790   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9791   //  while ( tmpFaceIt->more() )
9792   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9793   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9794   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9795     aMesh->RemoveElement(*tmpFaceIt);
9796
9797   if ( aResult != SEW_OK)
9798     return aResult;
9799
9800   list< int > nodeIDsToRemove;
9801   vector< const SMDS_MeshNode*> nodes;
9802   ElemFeatures elemType;
9803
9804   // loop on nodes replacement map
9805   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9806   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9807     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9808     {
9809       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9810       nodeIDsToRemove.push_back( nToRemove->GetID() );
9811       // loop on elements sharing nToRemove
9812       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9813       while ( invElemIt->more() ) {
9814         const SMDS_MeshElement* e = invElemIt->next();
9815         // get a new suite of nodes: make replacement
9816         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9817         nodes.resize( nbNodes );
9818         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9819         while ( nIt->more() ) {
9820           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9821           nnIt = nReplaceMap.find( n );
9822           if ( nnIt != nReplaceMap.end() ) {
9823             nbReplaced++;
9824             n = (*nnIt).second;
9825           }
9826           nodes[ i++ ] = n;
9827         }
9828         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9829         //         elemIDsToRemove.push_back( e->GetID() );
9830         //       else
9831         if ( nbReplaced )
9832         {
9833           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9834           aMesh->RemoveElement( e );
9835
9836           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9837           {
9838             AddToSameGroups( newElem, e, aMesh );
9839             if ( int aShapeId = e->getshapeId() )
9840               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9841           }
9842         }
9843       }
9844     }
9845
9846   Remove( nodeIDsToRemove, true );
9847
9848   return aResult;
9849 }
9850
9851 //================================================================================
9852 /*!
9853  * \brief Find corresponding nodes in two sets of faces
9854  * \param theSide1 - first face set
9855  * \param theSide2 - second first face
9856  * \param theFirstNode1 - a boundary node of set 1
9857  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9858  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9859  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9860  * \param nReplaceMap - output map of corresponding nodes
9861  * \return bool  - is a success or not
9862  */
9863 //================================================================================
9864
9865 #ifdef _DEBUG_
9866 //#define DEBUG_MATCHING_NODES
9867 #endif
9868
9869 SMESH_MeshEditor::Sew_Error
9870 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9871                                     set<const SMDS_MeshElement*>& theSide2,
9872                                     const SMDS_MeshNode*          theFirstNode1,
9873                                     const SMDS_MeshNode*          theFirstNode2,
9874                                     const SMDS_MeshNode*          theSecondNode1,
9875                                     const SMDS_MeshNode*          theSecondNode2,
9876                                     TNodeNodeMap &                nReplaceMap)
9877 {
9878   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9879
9880   nReplaceMap.clear();
9881   if ( theFirstNode1 != theFirstNode2 )
9882     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9883   if ( theSecondNode1 != theSecondNode2 )
9884     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9885
9886   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9887   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9888
9889   list< NLink > linkList[2];
9890   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9891   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9892
9893   // loop on links in linkList; find faces by links and append links
9894   // of the found faces to linkList
9895   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9896   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9897     NLink link[] = { *linkIt[0], *linkIt[1] };
9898     if ( linkSet.find( link[0] ) == linkSet.end() )
9899       continue;
9900
9901     // by links, find faces in the face sets,
9902     // and find indices of link nodes in the found faces;
9903     // in a face set, there is only one or no face sharing a link
9904     // ---------------------------------------------------------------
9905
9906     const SMDS_MeshElement* face[] = { 0, 0 };
9907     list<const SMDS_MeshNode*> notLinkNodes[2];
9908     //bool reverse[] = { false, false }; // order of notLinkNodes
9909     int nbNodes[2];
9910     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9911     {
9912       const SMDS_MeshNode* n1 = link[iSide].first;
9913       const SMDS_MeshNode* n2 = link[iSide].second;
9914       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9915       set< const SMDS_MeshElement* > facesOfNode1;
9916       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9917       {
9918         // during a loop of the first node, we find all faces around n1,
9919         // during a loop of the second node, we find one face sharing both n1 and n2
9920         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9921         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9922         while ( fIt->more() ) { // loop on faces sharing a node
9923           const SMDS_MeshElement* f = fIt->next();
9924           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9925               ! facesOfNode1.insert( f ).second ) // f encounters twice
9926           {
9927             if ( face[ iSide ] ) {
9928               MESSAGE( "2 faces per link " );
9929               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9930             }
9931             face[ iSide ] = f;
9932             faceSet->erase( f );
9933
9934             // get not link nodes
9935             int nbN = f->NbNodes();
9936             if ( f->IsQuadratic() )
9937               nbN /= 2;
9938             nbNodes[ iSide ] = nbN;
9939             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9940             int i1 = f->GetNodeIndex( n1 );
9941             int i2 = f->GetNodeIndex( n2 );
9942             int iEnd = nbN, iBeg = -1, iDelta = 1;
9943             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9944             if ( reverse ) {
9945               std::swap( iEnd, iBeg ); iDelta = -1;
9946             }
9947             int i = i2;
9948             while ( true ) {
9949               i += iDelta;
9950               if ( i == iEnd ) i = iBeg + iDelta;
9951               if ( i == i1 ) break;
9952               nodes.push_back ( f->GetNode( i ) );
9953             }
9954           }
9955         }
9956       }
9957     }
9958     // check similarity of elements of the sides
9959     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9960       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9961       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9962         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9963       }
9964       else {
9965         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9966       }
9967     }
9968
9969     // set nodes to merge
9970     // -------------------
9971
9972     if ( face[0] && face[1] )  {
9973       if ( nbNodes[0] != nbNodes[1] ) {
9974         MESSAGE("Diff nb of face nodes");
9975         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9976       }
9977 #ifdef DEBUG_MATCHING_NODES
9978       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9979                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9980                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9981 #endif
9982       int nbN = nbNodes[0];
9983       {
9984         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9985         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9986         for ( int i = 0 ; i < nbN - 2; ++i ) {
9987 #ifdef DEBUG_MATCHING_NODES
9988           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9989 #endif
9990           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9991         }
9992       }
9993
9994       // add other links of the face 1 to linkList
9995       // -----------------------------------------
9996
9997       const SMDS_MeshElement* f0 = face[0];
9998       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9999       for ( int i = 0; i < nbN; i++ )
10000       {
10001         const SMDS_MeshNode* n2 = f0->GetNode( i );
10002         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10003           linkSet.insert( SMESH_TLink( n1, n2 ));
10004         if ( !iter_isnew.second ) { // already in a set: no need to process
10005           linkSet.erase( iter_isnew.first );
10006         }
10007         else // new in set == encountered for the first time: add
10008         {
10009 #ifdef DEBUG_MATCHING_NODES
10010           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10011                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10012 #endif
10013           linkList[0].push_back ( NLink( n1, n2 ));
10014           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10015         }
10016         n1 = n2;
10017       }
10018     } // 2 faces found
10019   } // loop on link lists
10020
10021   return SEW_OK;
10022 }
10023
10024 namespace // automatically find theAffectedElems for DoubleNodes()
10025 {
10026   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10027
10028   //--------------------------------------------------------------------------------
10029   // Nodes shared by adjacent FissureBorder's.
10030   // 1 node  if FissureBorder separates faces
10031   // 2 nodes if FissureBorder separates volumes
10032   struct SubBorder
10033   {
10034     const SMDS_MeshNode* _nodes[2];
10035     int                  _nbNodes;
10036
10037     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10038     {
10039       _nodes[0] = n1;
10040       _nodes[1] = n2;
10041       _nbNodes = bool( n1 ) + bool( n2 );
10042       if ( _nbNodes == 2 && n1 > n2 )
10043         std::swap( _nodes[0], _nodes[1] );
10044     }
10045     bool operator<( const SubBorder& other ) const
10046     {
10047       for ( int i = 0; i < _nbNodes; ++i )
10048       {
10049         if ( _nodes[i] < other._nodes[i] ) return true;
10050         if ( _nodes[i] > other._nodes[i] ) return false;
10051       }
10052       return false;
10053     }
10054   };
10055
10056   //--------------------------------------------------------------------------------
10057   // Map a SubBorder to all FissureBorder it bounds
10058   struct FissureBorder;
10059   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10060   typedef TBorderLinks::iterator                               TMappedSub;
10061
10062   //--------------------------------------------------------------------------------
10063   /*!
10064    * \brief Element border (volume facet or face edge) at a fissure
10065    */
10066   struct FissureBorder
10067   {
10068     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10069     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10070
10071     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10072     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10073
10074     FissureBorder( FissureBorder && from ) // move constructor
10075     {
10076       std::swap( _nodes,       from._nodes );
10077       std::swap( _sortedNodes, from._sortedNodes );
10078       _elems[0] = from._elems[0];
10079       _elems[1] = from._elems[1];
10080     }
10081
10082     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10083                    std::vector< const SMDS_MeshElement* > & adjElems)
10084       : _nodes( elemToDuplicate->NbCornerNodes() )
10085     {
10086       for ( size_t i = 0; i < _nodes.size(); ++i )
10087         _nodes[i] = elemToDuplicate->GetNode( i );
10088
10089       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10090       findAdjacent( type, adjElems );
10091     }
10092
10093     FissureBorder( const SMDS_MeshNode**                    nodes,
10094                    const size_t                             nbNodes,
10095                    const SMDSAbs_ElementType                adjElemsType,
10096                    std::vector< const SMDS_MeshElement* > & adjElems)
10097       : _nodes( nodes, nodes + nbNodes )
10098     {
10099       findAdjacent( adjElemsType, adjElems );
10100     }
10101
10102     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10103                        std::vector< const SMDS_MeshElement* > & adjElems)
10104     {
10105       _elems[0] = _elems[1] = 0;
10106       adjElems.clear();
10107       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10108         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10109           _elems[i] = adjElems[i];
10110     }
10111
10112     bool operator<( const FissureBorder& other ) const
10113     {
10114       return GetSortedNodes() < other.GetSortedNodes();
10115     }
10116
10117     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10118     {
10119       if ( _sortedNodes.empty() && !_nodes.empty() )
10120       {
10121         FissureBorder* me = const_cast<FissureBorder*>( this );
10122         me->_sortedNodes = me->_nodes;
10123         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10124       }
10125       return _sortedNodes;
10126     }
10127
10128     size_t NbSub() const
10129     {
10130       return _nodes.size();
10131     }
10132
10133     SubBorder Sub(size_t i) const
10134     {
10135       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10136     }
10137
10138     void AddSelfTo( TBorderLinks& borderLinks )
10139     {
10140       _mappedSubs.resize( NbSub() );
10141       for ( size_t i = 0; i < NbSub(); ++i )
10142       {
10143         TBorderLinks::iterator s2b =
10144           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10145         s2b->second.push_back( this );
10146         _mappedSubs[ i ] = s2b;
10147       }
10148     }
10149
10150     void Clear()
10151     {
10152       _nodes.clear();
10153     }
10154
10155     const SMDS_MeshElement* GetMarkedElem() const
10156     {
10157       if ( _nodes.empty() ) return 0; // cleared
10158       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10159       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10160       return 0;
10161     }
10162
10163     gp_XYZ GetNorm() const // normal to the border
10164     {
10165       gp_XYZ norm;
10166       if ( _nodes.size() == 2 )
10167       {
10168         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10169         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10170           avgNorm += norm;
10171         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10172           avgNorm += norm;
10173
10174         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10175         norm = bordDir ^ avgNorm;
10176       }
10177       else
10178       {
10179         SMESH_NodeXYZ p0( _nodes[0] );
10180         SMESH_NodeXYZ p1( _nodes[1] );
10181         SMESH_NodeXYZ p2( _nodes[2] );
10182         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10183       }
10184       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10185         norm.Reverse();
10186
10187       return norm;
10188     }
10189
10190     void ChooseSide() // mark an _elem located at positive side of fissure
10191     {
10192       _elems[0]->setIsMarked( true );
10193       gp_XYZ norm = GetNorm();
10194       double maxX = norm.Coord(1);
10195       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10196       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10197       if ( maxX < 0 )
10198       {
10199         _elems[0]->setIsMarked( false );
10200         _elems[1]->setIsMarked( true );
10201       }
10202     }
10203
10204   }; // struct FissureBorder
10205
10206   //--------------------------------------------------------------------------------
10207   /*!
10208    * \brief Classifier of elements at fissure edge
10209    */
10210   class FissureNormal
10211   {
10212     std::vector< gp_XYZ > _normals;
10213     bool                  _bothIn;
10214
10215   public:
10216     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10217     {
10218       _bothIn = false;
10219       _normals.reserve(2);
10220       _normals.push_back( bord.GetNorm() );
10221       if ( _normals.size() == 2 )
10222         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10223     }
10224
10225     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10226     {
10227       bool isIn = false;
10228       switch ( _normals.size() ) {
10229       case 1:
10230       {
10231         isIn = !isOut( n, _normals[0], elem );
10232         break;
10233       }
10234       case 2:
10235       {
10236         bool in1 = !isOut( n, _normals[0], elem );
10237         bool in2 = !isOut( n, _normals[1], elem );
10238         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10239       }
10240       }
10241       return isIn;
10242     }
10243   };
10244
10245   //================================================================================
10246   /*!
10247    * \brief Classify an element by a plane passing through a node
10248    */
10249   //================================================================================
10250
10251   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10252   {
10253     SMESH_NodeXYZ p = n;
10254     double sumDot = 0;
10255     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10256     {
10257       SMESH_NodeXYZ pi = elem->GetNode( i );
10258       sumDot += norm * ( pi - p );
10259     }
10260     return sumDot < -1e-100;
10261   }
10262
10263   //================================================================================
10264   /*!
10265    * \brief Find FissureBorder's by nodes to duplicate
10266    */
10267   //================================================================================
10268
10269   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10270                            std::vector< FissureBorder > & theFissureBorders )
10271   {
10272     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10273     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10274     if ( !n ) return;
10275     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10276     if ( n->NbInverseElements( elemType ) == 0 )
10277     {
10278       elemType = SMDSAbs_Face;
10279       if ( n->NbInverseElements( elemType ) == 0 )
10280         return;
10281     }
10282     // unmark elements touching the fissure
10283     for ( ; nIt != theNodes.end(); ++nIt )
10284       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10285
10286     // loop on elements touching the fissure to get their borders belonging to the fissure
10287     std::set< FissureBorder >              fissureBorders;
10288     std::vector< const SMDS_MeshElement* > adjElems;
10289     std::vector< const SMDS_MeshNode* >    nodes;
10290     SMDS_VolumeTool volTool;
10291     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10292     {
10293       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10294       while ( invIt->more() )
10295       {
10296         const SMDS_MeshElement* eInv = invIt->next();
10297         if ( eInv->isMarked() ) continue;
10298         eInv->setIsMarked( true );
10299
10300         if ( elemType == SMDSAbs_Volume )
10301         {
10302           volTool.Set( eInv );
10303           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10304           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10305           {
10306             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10307             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10308             nodes.clear();
10309             bool allOnFissure = true;
10310             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10311               if (( allOnFissure = theNodes.count( nn[ iN ])))
10312                 nodes.push_back( nn[ iN ]);
10313             if ( allOnFissure )
10314               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10315                                                                elemType, adjElems )));
10316           }
10317         }
10318         else // elemType == SMDSAbs_Face
10319         {
10320           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10321           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10322           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10323           {
10324             nn[1]      = eInv->GetNode( iN );
10325             onFissure1 = theNodes.count( nn[1] );
10326             if ( onFissure0 && onFissure1 )
10327               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10328             nn[0]      = nn[1];
10329             onFissure0 = onFissure1;
10330           }
10331         }
10332       }
10333     }
10334
10335     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10336     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10337     for ( ; bord != fissureBorders.end(); ++bord )
10338     {
10339       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10340     }
10341     return;
10342   } // findFissureBorders()
10343
10344   //================================================================================
10345   /*!
10346    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10347    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10348    *  \param [in] theNodesNot - nodes not to duplicate
10349    *  \param [out] theAffectedElems - the found elements
10350    */
10351   //================================================================================
10352
10353   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10354                           TIDSortedElemSet&       theAffectedElems)
10355   {
10356     if ( theElemsOrNodes.empty() ) return;
10357
10358     // find FissureBorder's
10359
10360     std::vector< FissureBorder >           fissure;
10361     std::vector< const SMDS_MeshElement* > elemsByFacet;
10362
10363     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10364     if ( (*elIt)->GetType() == SMDSAbs_Node )
10365     {
10366       findFissureBorders( theElemsOrNodes, fissure );
10367     }
10368     else
10369     {
10370       fissure.reserve( theElemsOrNodes.size() );
10371       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10372         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10373     }
10374     if ( fissure.empty() )
10375       return;
10376
10377     // fill borderLinks
10378
10379     TBorderLinks borderLinks;
10380
10381     for ( size_t i = 0; i < fissure.size(); ++i )
10382     {
10383       fissure[i].AddSelfTo( borderLinks );
10384     }
10385
10386     // get theAffectedElems
10387
10388     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10389     for ( size_t i = 0; i < fissure.size(); ++i )
10390       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10391       {
10392         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10393                                         false, /*markElem=*/true );
10394       }
10395
10396     std::vector<const SMDS_MeshNode *>                 facetNodes;
10397     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10398     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10399
10400     // choose a side of fissure
10401     fissure[0].ChooseSide();
10402     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10403
10404     size_t nbCheckedBorders = 0;
10405     while ( nbCheckedBorders < fissure.size() )
10406     {
10407       // find a FissureBorder to treat
10408       FissureBorder* bord = 0;
10409       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10410         if ( fissure[i].GetMarkedElem() )
10411           bord = & fissure[i];
10412       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10413         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10414         {
10415           bord = & fissure[i];
10416           bord->ChooseSide();
10417           theAffectedElems.insert( bord->GetMarkedElem() );
10418         }
10419       if ( !bord ) return;
10420       ++nbCheckedBorders;
10421
10422       // treat FissureBorder's linked to bord
10423       fissureNodes.clear();
10424       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10425       for ( size_t i = 0; i < bord->NbSub(); ++i )
10426       {
10427         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10428         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10429         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10430         const SubBorder&                          sb = l2b->first;
10431         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10432
10433         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10434         {
10435           for ( int j = 0; j < sb._nbNodes; ++j )
10436             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10437           continue;
10438         }
10439
10440         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10441         // until an elem adjacent to a neighbour FissureBorder is found
10442         facetNodes.clear();
10443         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10444         facetNodes.resize( sb._nbNodes + 1 );
10445
10446         while ( bordElem )
10447         {
10448           // check if bordElem is adjacent to a neighbour FissureBorder
10449           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10450           {
10451             FissureBorder* bord2 = linkedBorders[j];
10452             if ( bord2 == bord ) continue;
10453             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10454               bordElem = 0;
10455             else
10456               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10457           }
10458           if ( !bordElem )
10459             break;
10460
10461           // find the next bordElem
10462           const SMDS_MeshElement* nextBordElem = 0;
10463           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10464           {
10465             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10466             if ( fissureNodes.count( n )) continue;
10467
10468             facetNodes[ sb._nbNodes ] = n;
10469             elemsByFacet.clear();
10470             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10471             {
10472               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10473                 if ( elemsByFacet[ iE ] != bordElem &&
10474                      !elemsByFacet[ iE ]->isMarked() )
10475                 {
10476                   theAffectedElems.insert( elemsByFacet[ iE ]);
10477                   elemsByFacet[ iE ]->setIsMarked( true );
10478                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10479                     nextBordElem = elemsByFacet[ iE ];
10480                 }
10481             }
10482           }
10483           bordElem = nextBordElem;
10484
10485         } // while ( bordElem )
10486
10487         linkedBorders.clear(); // not to treat this link any more
10488
10489       } // loop on SubBorder's of a FissureBorder
10490
10491       bord->Clear();
10492
10493     } // loop on FissureBorder's
10494
10495
10496     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10497
10498     // mark nodes of theAffectedElems
10499     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10500
10501     // unmark nodes of the fissure
10502     elIt = theElemsOrNodes.begin();
10503     if ( (*elIt)->GetType() == SMDSAbs_Node )
10504       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10505     else
10506       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10507
10508     std::vector< gp_XYZ > normVec;
10509
10510     // loop on nodes of the fissure, add elements having marked nodes
10511     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10512     {
10513       const SMDS_MeshElement* e = (*elIt);
10514       if ( e->GetType() != SMDSAbs_Node )
10515         e->setIsMarked( true ); // avoid adding a fissure element
10516
10517       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10518       {
10519         const SMDS_MeshNode* n = e->GetNode( iN );
10520         if ( fissEdgeNodes2Norm.count( n ))
10521           continue;
10522
10523         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10524         while ( invIt->more() )
10525         {
10526           const SMDS_MeshElement* eInv = invIt->next();
10527           if ( eInv->isMarked() ) continue;
10528           eInv->setIsMarked( true );
10529
10530           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10531           while( nIt->more() )
10532             if ( nIt->next()->isMarked())
10533             {
10534               theAffectedElems.insert( eInv );
10535               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10536               n->setIsMarked( false );
10537               break;
10538             }
10539         }
10540       }
10541     }
10542
10543     // add elements on the fissure edge
10544     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10545     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10546     {
10547       const SMDS_MeshNode* edgeNode = n2N->first;
10548       const FissureNormal & normals = n2N->second;
10549
10550       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10551       while ( invIt->more() )
10552       {
10553         const SMDS_MeshElement* eInv = invIt->next();
10554         if ( eInv->isMarked() ) continue;
10555         eInv->setIsMarked( true );
10556
10557         // classify eInv using normals
10558         bool toAdd = normals.IsIn( edgeNode, eInv );
10559         if ( toAdd ) // check if all nodes lie on the fissure edge
10560         {
10561           bool notOnEdge = false;
10562           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10563             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10564           toAdd = notOnEdge;
10565         }
10566         if ( toAdd )
10567         {
10568           theAffectedElems.insert( eInv );
10569         }
10570       }
10571     }
10572
10573     return;
10574   } // findAffectedElems()
10575 } // namespace
10576
10577 //================================================================================
10578 /*!
10579  * \brief Create elements equal (on same nodes) to given ones
10580  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10581  *              elements of the uppest dimension are duplicated.
10582  */
10583 //================================================================================
10584
10585 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10586 {
10587   ClearLastCreated();
10588   SMESHDS_Mesh* mesh = GetMeshDS();
10589
10590   // get an element type and an iterator over elements
10591
10592   SMDSAbs_ElementType type = SMDSAbs_All;
10593   SMDS_ElemIteratorPtr elemIt;
10594   if ( theElements.empty() )
10595   {
10596     if ( mesh->NbNodes() == 0 )
10597       return;
10598     // get most complex type
10599     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10600       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10601       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10602     };
10603     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10604       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10605       {
10606         type = types[i];
10607         elemIt = mesh->elementsIterator( type );
10608         break;
10609       }
10610   }
10611   else
10612   {
10613     //type = (*theElements.begin())->GetType();
10614     elemIt = SMESHUtils::elemSetIterator( theElements );
10615   }
10616
10617   // un-mark all elements to avoid duplicating just created elements
10618   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10619
10620   // duplicate elements
10621
10622   ElemFeatures elemType;
10623
10624   vector< const SMDS_MeshNode* > nodes;
10625   while ( elemIt->more() )
10626   {
10627     const SMDS_MeshElement* elem = elemIt->next();
10628     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10629         ( elem->isMarked() ))
10630       continue;
10631
10632     elemType.Init( elem, /*basicOnly=*/false );
10633     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10634
10635     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10636       newElem->setIsMarked( true );
10637   }
10638 }
10639
10640 //================================================================================
10641 /*!
10642   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643   \param theElems - the list of elements (edges or faces) to be replicated
10644   The nodes for duplication could be found from these elements
10645   \param theNodesNot - list of nodes to NOT replicate
10646   \param theAffectedElems - the list of elements (cells and edges) to which the
10647   replicated nodes should be associated to.
10648   \return TRUE if operation has been completed successfully, FALSE otherwise
10649 */
10650 //================================================================================
10651
10652 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10653                                     const TIDSortedElemSet& theNodesNot,
10654                                     const TIDSortedElemSet& theAffectedElems )
10655 {
10656   ClearLastCreated();
10657
10658   if ( theElems.size() == 0 )
10659     return false;
10660
10661   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10662   if ( !aMeshDS )
10663     return false;
10664
10665   bool res = false;
10666   TNodeNodeMap anOldNodeToNewNode;
10667   // duplicate elements and nodes
10668   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10669   // replce nodes by duplications
10670   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10671   return res;
10672 }
10673
10674 //================================================================================
10675 /*!
10676   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10677   \param theMeshDS - mesh instance
10678   \param theElems - the elements replicated or modified (nodes should be changed)
10679   \param theNodesNot - nodes to NOT replicate
10680   \param theNodeNodeMap - relation of old node to new created node
10681   \param theIsDoubleElem - flag os to replicate element or modify
10682   \return TRUE if operation has been completed successfully, FALSE otherwise
10683 */
10684 //================================================================================
10685
10686 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10687                                    const TIDSortedElemSet& theElems,
10688                                    const TIDSortedElemSet& theNodesNot,
10689                                    TNodeNodeMap&           theNodeNodeMap,
10690                                    const bool              theIsDoubleElem )
10691 {
10692   // iterate through element and duplicate them (by nodes duplication)
10693   bool res = false;
10694   std::vector<const SMDS_MeshNode*> newNodes;
10695   ElemFeatures elemType;
10696
10697   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10698   for ( ;  elemItr != theElems.end(); ++elemItr )
10699   {
10700     const SMDS_MeshElement* anElem = *elemItr;
10701     // if (!anElem)
10702     //   continue;
10703
10704     // duplicate nodes to duplicate element
10705     bool isDuplicate = false;
10706     newNodes.resize( anElem->NbNodes() );
10707     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10708     int ind = 0;
10709     while ( anIter->more() )
10710     {
10711       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10712       const SMDS_MeshNode*  aNewNode = aCurrNode;
10713       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10714       if ( n2n != theNodeNodeMap.end() )
10715       {
10716         aNewNode = n2n->second;
10717       }
10718       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10719       {
10720         // duplicate node
10721         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10722         copyPosition( aCurrNode, aNewNode );
10723         theNodeNodeMap[ aCurrNode ] = aNewNode;
10724         myLastCreatedNodes.push_back( aNewNode );
10725       }
10726       isDuplicate |= (aCurrNode != aNewNode);
10727       newNodes[ ind++ ] = aNewNode;
10728     }
10729     if ( !isDuplicate )
10730       continue;
10731
10732     if ( theIsDoubleElem )
10733       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10734     else
10735       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10736
10737     res = true;
10738   }
10739   return res;
10740 }
10741
10742 //================================================================================
10743 /*!
10744   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10745   \param theNodes - identifiers of nodes to be doubled
10746   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10747   nodes. If list of element identifiers is empty then nodes are doubled but
10748   they not assigned to elements
10749   \return TRUE if operation has been completed successfully, FALSE otherwise
10750 */
10751 //================================================================================
10752
10753 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10754                                     const std::list< int >& theListOfModifiedElems )
10755 {
10756   ClearLastCreated();
10757
10758   if ( theListOfNodes.size() == 0 )
10759     return false;
10760
10761   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10762   if ( !aMeshDS )
10763     return false;
10764
10765   // iterate through nodes and duplicate them
10766
10767   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10768
10769   std::list< int >::const_iterator aNodeIter;
10770   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10771   {
10772     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10773     if ( !aNode )
10774       continue;
10775
10776     // duplicate node
10777
10778     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10779     if ( aNewNode )
10780     {
10781       copyPosition( aNode, aNewNode );
10782       anOldNodeToNewNode[ aNode ] = aNewNode;
10783       myLastCreatedNodes.push_back( aNewNode );
10784     }
10785   }
10786
10787   // Change nodes of elements
10788
10789   std::vector<const SMDS_MeshNode*> aNodeArr;
10790
10791   std::list< int >::const_iterator anElemIter;
10792   for ( anElemIter =  theListOfModifiedElems.begin();
10793         anElemIter != theListOfModifiedElems.end();
10794         anElemIter++ )
10795   {
10796     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10797     if ( !anElem )
10798       continue;
10799
10800     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10801     for( size_t i = 0; i < aNodeArr.size(); ++i )
10802     {
10803       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10804         anOldNodeToNewNode.find( aNodeArr[ i ]);
10805       if ( n2n != anOldNodeToNewNode.end() )
10806         aNodeArr[ i ] = n2n->second;
10807     }
10808     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10809   }
10810
10811   return true;
10812 }
10813
10814 namespace {
10815
10816   //================================================================================
10817   /*!
10818     \brief Check if element located inside shape
10819     \return TRUE if IN or ON shape, FALSE otherwise
10820   */
10821   //================================================================================
10822
10823   template<class Classifier>
10824   bool isInside(const SMDS_MeshElement* theElem,
10825                 Classifier&             theClassifier,
10826                 const double            theTol)
10827   {
10828     gp_XYZ centerXYZ (0, 0, 0);
10829     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10830       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10831
10832     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10833     theClassifier.Perform(aPnt, theTol);
10834     TopAbs_State aState = theClassifier.State();
10835     return (aState == TopAbs_IN || aState == TopAbs_ON );
10836   }
10837
10838   //================================================================================
10839   /*!
10840    * \brief Classifier of the 3D point on the TopoDS_Face
10841    *        with interaface suitable for isInside()
10842    */
10843   //================================================================================
10844
10845   struct _FaceClassifier
10846   {
10847     Extrema_ExtPS       _extremum;
10848     BRepAdaptor_Surface _surface;
10849     TopAbs_State        _state;
10850
10851     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10852     {
10853       _extremum.Initialize( _surface,
10854                             _surface.FirstUParameter(), _surface.LastUParameter(),
10855                             _surface.FirstVParameter(), _surface.LastVParameter(),
10856                             _surface.Tolerance(), _surface.Tolerance() );
10857     }
10858     void Perform(const gp_Pnt& aPnt, double theTol)
10859     {
10860       theTol *= theTol;
10861       _state = TopAbs_OUT;
10862       _extremum.Perform(aPnt);
10863       if ( _extremum.IsDone() )
10864         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10865           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10866     }
10867     TopAbs_State State() const
10868     {
10869       return _state;
10870     }
10871   };
10872 }
10873
10874 //================================================================================
10875 /*!
10876   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10877   This method is the first step of DoubleNodeElemGroupsInRegion.
10878   \param theElems - list of groups of elements (edges or faces) to be replicated
10879   \param theNodesNot - list of groups of nodes not to replicated
10880   \param theShape - shape to detect affected elements (element which geometric center
10881          located on or inside shape). If the shape is null, detection is done on faces orientations
10882          (select elements with a gravity center on the side given by faces normals).
10883          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10884          The replicated nodes should be associated to affected elements.
10885   \return true
10886   \sa DoubleNodeElemGroupsInRegion()
10887 */
10888 //================================================================================
10889
10890 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10891                                                    const TIDSortedElemSet& theNodesNot,
10892                                                    const TopoDS_Shape&     theShape,
10893                                                    TIDSortedElemSet&       theAffectedElems)
10894 {
10895   if ( theShape.IsNull() )
10896   {
10897     findAffectedElems( theElems, theAffectedElems );
10898   }
10899   else
10900   {
10901     const double aTol = Precision::Confusion();
10902     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10903     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10904     if ( theShape.ShapeType() == TopAbs_SOLID )
10905     {
10906       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10907       bsc3d->PerformInfinitePoint(aTol);
10908     }
10909     else if (theShape.ShapeType() == TopAbs_FACE )
10910     {
10911       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10912     }
10913
10914     // iterates on indicated elements and get elements by back references from their nodes
10915     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10916     for ( ;  elemItr != theElems.end(); ++elemItr )
10917     {
10918       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10919       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10920       while ( nodeItr->more() )
10921       {
10922         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10923         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10924           continue;
10925         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10926         while ( backElemItr->more() )
10927         {
10928           const SMDS_MeshElement* curElem = backElemItr->next();
10929           if ( curElem && theElems.find(curElem) == theElems.end() &&
10930                ( bsc3d.get() ?
10931                  isInside( curElem, *bsc3d, aTol ) :
10932                  isInside( curElem, *aFaceClassifier, aTol )))
10933             theAffectedElems.insert( curElem );
10934         }
10935       }
10936     }
10937   }
10938   return true;
10939 }
10940
10941 //================================================================================
10942 /*!
10943   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10944   \param theElems - group of of elements (edges or faces) to be replicated
10945   \param theNodesNot - group of nodes not to replicate
10946   \param theShape - shape to detect affected elements (element which geometric center
10947   located on or inside shape).
10948   The replicated nodes should be associated to affected elements.
10949   \return TRUE if operation has been completed successfully, FALSE otherwise
10950 */
10951 //================================================================================
10952
10953 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10954                                             const TIDSortedElemSet& theNodesNot,
10955                                             const TopoDS_Shape&     theShape )
10956 {
10957   if ( theShape.IsNull() )
10958     return false;
10959
10960   const double aTol = Precision::Confusion();
10961   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10962   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10963   if ( theShape.ShapeType() == TopAbs_SOLID )
10964   {
10965     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10966     bsc3d->PerformInfinitePoint(aTol);
10967   }
10968   else if (theShape.ShapeType() == TopAbs_FACE )
10969   {
10970     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10971   }
10972
10973   // iterates on indicated elements and get elements by back references from their nodes
10974   TIDSortedElemSet anAffected;
10975   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10976   for ( ;  elemItr != theElems.end(); ++elemItr )
10977   {
10978     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10979     if (!anElem)
10980       continue;
10981
10982     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10983     while ( nodeItr->more() )
10984     {
10985       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10986       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10987         continue;
10988       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10989       while ( backElemItr->more() )
10990       {
10991         const SMDS_MeshElement* curElem = backElemItr->next();
10992         if ( curElem && theElems.find(curElem) == theElems.end() &&
10993              ( bsc3d ?
10994                isInside( curElem, *bsc3d, aTol ) :
10995                isInside( curElem, *aFaceClassifier, aTol )))
10996           anAffected.insert( curElem );
10997       }
10998     }
10999   }
11000   return DoubleNodes( theElems, theNodesNot, anAffected );
11001 }
11002
11003 /*!
11004  *  \brief compute an oriented angle between two planes defined by four points.
11005  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11006  *  @param p0 base of the rotation axe
11007  *  @param p1 extremity of the rotation axe
11008  *  @param g1 belongs to the first plane
11009  *  @param g2 belongs to the second plane
11010  */
11011 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11012 {
11013   gp_Vec vref(p0, p1);
11014   gp_Vec v1(p0, g1);
11015   gp_Vec v2(p0, g2);
11016   gp_Vec n1 = vref.Crossed(v1);
11017   gp_Vec n2 = vref.Crossed(v2);
11018   try {
11019     return n2.AngleWithRef(n1, vref);
11020   }
11021   catch ( Standard_Failure ) {
11022   }
11023   return Max( v1.Magnitude(), v2.Magnitude() );
11024 }
11025
11026 /*!
11027  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11028  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11029  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11030  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11031  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11032  * 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.
11033  * 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.
11034  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11035  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11036  * \param theElems - list of groups of volumes, where a group of volume is a set of
11037  *        SMDS_MeshElements sorted by Id.
11038  * \param createJointElems - if TRUE, create the elements
11039  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11040  *        the boundary between \a theDomains and the rest mesh
11041  * \return TRUE if operation has been completed successfully, FALSE otherwise
11042  */
11043 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11044                                                      bool                                 createJointElems,
11045                                                      bool                                 onAllBoundaries)
11046 {
11047   // MESSAGE("----------------------------------------------");
11048   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11049   // MESSAGE("----------------------------------------------");
11050
11051   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11052   meshDS->BuildDownWardConnectivity(true);
11053   CHRONO(50);
11054   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11055
11056   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11057   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11058   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11059
11060   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11061   std::map<int,int>celldom; // cell vtkId --> domain
11062   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11063   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11064   faceDomains.clear();
11065   celldom.clear();
11066   cellDomains.clear();
11067   nodeDomains.clear();
11068   std::map<int,int> emptyMap;
11069   std::set<int> emptySet;
11070   emptyMap.clear();
11071
11072   //MESSAGE(".. Number of domains :"<<theElems.size());
11073
11074   TIDSortedElemSet theRestDomElems;
11075   const int iRestDom  = -1;
11076   const int idom0     = onAllBoundaries ? iRestDom : 0;
11077   const int nbDomains = theElems.size();
11078
11079   // Check if the domains do not share an element
11080   for (int idom = 0; idom < nbDomains-1; idom++)
11081   {
11082     //       MESSAGE("... Check of domain #" << idom);
11083     const TIDSortedElemSet& domain = theElems[idom];
11084     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11085     for (; elemItr != domain.end(); ++elemItr)
11086     {
11087       const SMDS_MeshElement* anElem = *elemItr;
11088       int idombisdeb = idom + 1 ;
11089       // check if the element belongs to a domain further in the list
11090       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11091       {
11092         const TIDSortedElemSet& domainbis = theElems[idombis];
11093         if ( domainbis.count( anElem ))
11094         {
11095           MESSAGE(".... Domain #" << idom);
11096           MESSAGE(".... Domain #" << idombis);
11097           throw SALOME_Exception("The domains are not disjoint.");
11098           return false ;
11099         }
11100       }
11101     }
11102   }
11103
11104   for (int idom = 0; idom < nbDomains; idom++)
11105   {
11106
11107     // --- build a map (face to duplicate --> volume to modify)
11108     //     with all the faces shared by 2 domains (group of elements)
11109     //     and corresponding volume of this domain, for each shared face.
11110     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11111
11112     //MESSAGE("... Neighbors of domain #" << idom);
11113     const TIDSortedElemSet& domain = theElems[idom];
11114     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11115     for (; elemItr != domain.end(); ++elemItr)
11116     {
11117       const SMDS_MeshElement* anElem = *elemItr;
11118       if (!anElem)
11119         continue;
11120       int vtkId = anElem->GetVtkID();
11121       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11122       int neighborsVtkIds[NBMAXNEIGHBORS];
11123       int downIds[NBMAXNEIGHBORS];
11124       unsigned char downTypes[NBMAXNEIGHBORS];
11125       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11126       for (int n = 0; n < nbNeighbors; n++)
11127       {
11128         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11129         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11130         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11131         {
11132           bool ok = false;
11133           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11134           {
11135             // MESSAGE("Domain " << idombis);
11136             const TIDSortedElemSet& domainbis = theElems[idombis];
11137             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11138           }
11139           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11140           {
11141             DownIdType face(downIds[n], downTypes[n]);
11142             if (!faceDomains[face].count(idom))
11143             {
11144               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11145               celldom[vtkId] = idom;
11146               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11147             }
11148             if ( !ok )
11149             {
11150               theRestDomElems.insert( elem );
11151               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11152               celldom[neighborsVtkIds[n]] = iRestDom;
11153             }
11154           }
11155         }
11156       }
11157     }
11158   }
11159
11160   //MESSAGE("Number of shared faces " << faceDomains.size());
11161   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11162
11163   // --- explore the shared faces domain by domain,
11164   //     explore the nodes of the face and see if they belong to a cell in the domain,
11165   //     which has only a node or an edge on the border (not a shared face)
11166
11167   for (int idomain = idom0; idomain < nbDomains; idomain++)
11168   {
11169     //MESSAGE("Domain " << idomain);
11170     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11171     itface = faceDomains.begin();
11172     for (; itface != faceDomains.end(); ++itface)
11173     {
11174       const std::map<int, int>& domvol = itface->second;
11175       if (!domvol.count(idomain))
11176         continue;
11177       DownIdType face = itface->first;
11178       //MESSAGE(" --- face " << face.cellId);
11179       std::set<int> oldNodes;
11180       oldNodes.clear();
11181       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11182       std::set<int>::iterator itn = oldNodes.begin();
11183       for (; itn != oldNodes.end(); ++itn)
11184       {
11185         int oldId = *itn;
11186         //MESSAGE("     node " << oldId);
11187         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11188         for (int i=0; i<l.ncells; i++)
11189         {
11190           int vtkId = l.cells[i];
11191           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11192           if (!domain.count(anElem))
11193             continue;
11194           int vtkType = grid->GetCellType(vtkId);
11195           int downId = grid->CellIdToDownId(vtkId);
11196           if (downId < 0)
11197           {
11198             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11199             continue; // not OK at this stage of the algorithm:
11200             //no cells created after BuildDownWardConnectivity
11201           }
11202           DownIdType aCell(downId, vtkType);
11203           cellDomains[aCell][idomain] = vtkId;
11204           celldom[vtkId] = idomain;
11205           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11206         }
11207       }
11208     }
11209   }
11210
11211   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11212   //     for each shared face, get the nodes
11213   //     for each node, for each domain of the face, create a clone of the node
11214
11215   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11216   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11217   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11218
11219   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11220   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11221   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11222
11223   //MESSAGE(".. Duplication of the nodes");
11224   for (int idomain = idom0; idomain < nbDomains; idomain++)
11225   {
11226     itface = faceDomains.begin();
11227     for (; itface != faceDomains.end(); ++itface)
11228     {
11229       const std::map<int, int>& domvol = itface->second;
11230       if (!domvol.count(idomain))
11231         continue;
11232       DownIdType face = itface->first;
11233       //MESSAGE(" --- face " << face.cellId);
11234       std::set<int> oldNodes;
11235       oldNodes.clear();
11236       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237       std::set<int>::iterator itn = oldNodes.begin();
11238       for (; itn != oldNodes.end(); ++itn)
11239       {
11240         int oldId = *itn;
11241         if (nodeDomains[oldId].empty())
11242         {
11243           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11244           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11245         }
11246         std::map<int, int>::const_iterator itdom = domvol.begin();
11247         for (; itdom != domvol.end(); ++itdom)
11248         {
11249           int idom = itdom->first;
11250           //MESSAGE("         domain " << idom);
11251           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11252           {
11253             if (nodeDomains[oldId].size() >= 2) // a multiple node
11254             {
11255               vector<int> orderedDoms;
11256               //MESSAGE("multiple node " << oldId);
11257               if (mutipleNodes.count(oldId))
11258                 orderedDoms = mutipleNodes[oldId];
11259               else
11260               {
11261                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11262                 for (; it != nodeDomains[oldId].end(); ++it)
11263                   orderedDoms.push_back(it->first);
11264               }
11265               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11266               //stringstream txt;
11267               //for (int i=0; i<orderedDoms.size(); i++)
11268               //  txt << orderedDoms[i] << " ";
11269               //MESSAGE("orderedDoms " << txt.str());
11270               mutipleNodes[oldId] = orderedDoms;
11271             }
11272             double *coords = grid->GetPoint(oldId);
11273             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11274             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11275             int newId = newNode->GetVtkID();
11276             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11277             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11278           }
11279         }
11280       }
11281     }
11282   }
11283
11284   //MESSAGE(".. Creation of elements");
11285   for (int idomain = idom0; idomain < nbDomains; idomain++)
11286   {
11287     itface = faceDomains.begin();
11288     for (; itface != faceDomains.end(); ++itface)
11289     {
11290       std::map<int, int> domvol = itface->second;
11291       if (!domvol.count(idomain))
11292         continue;
11293       DownIdType face = itface->first;
11294       //MESSAGE(" --- face " << face.cellId);
11295       std::set<int> oldNodes;
11296       oldNodes.clear();
11297       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11298       int nbMultipleNodes = 0;
11299       std::set<int>::iterator itn = oldNodes.begin();
11300       for (; itn != oldNodes.end(); ++itn)
11301       {
11302         int oldId = *itn;
11303         if (mutipleNodes.count(oldId))
11304           nbMultipleNodes++;
11305       }
11306       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11307       {
11308         //MESSAGE("multiple Nodes detected on a shared face");
11309         int downId = itface->first.cellId;
11310         unsigned char cellType = itface->first.cellType;
11311         // --- shared edge or shared face ?
11312         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11313         {
11314           int nodes[3];
11315           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11316           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11317             if (mutipleNodes.count(nodes[i]))
11318               if (!mutipleNodesToFace.count(nodes[i]))
11319                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11320         }
11321         else // shared face (between two volumes)
11322         {
11323           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11324           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11325           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11326           for (int ie =0; ie < nbEdges; ie++)
11327           {
11328             int nodes[3];
11329             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11330             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11331             {
11332               vector<int> vn0 = mutipleNodes[nodes[0]];
11333               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11334               vector<int> doms;
11335               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11336                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11337                   if ( vn0[i0] == vn1[i1] )
11338                     doms.push_back( vn0[ i0 ]);
11339               if ( doms.size() > 2 )
11340               {
11341                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11342                 double *coords = grid->GetPoint(nodes[0]);
11343                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11344                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11345                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11346                 gp_Pnt gref;
11347                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11348                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11349                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11350                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11351                 for ( size_t id = 0; id < doms.size(); id++ )
11352                 {
11353                   int idom = doms[id];
11354                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11355                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11356                   {
11357                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11358                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11359                     if (domain.count(elem))
11360                     {
11361                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11362                       domvol[idom] = (SMDS_MeshVolume*) svol;
11363                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11364                       double values[3] = { 0,0,0 };
11365                       vtkIdType npts = 0;
11366                       vtkIdType* pts = 0;
11367                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11368                       for ( vtkIdType i = 0; i < npts; ++i )
11369                       {
11370                         double *coords = grid->GetPoint( pts[i] );
11371                         for ( int j = 0; j < 3; ++j )
11372                           values[j] += coords[j] / npts;
11373                       }
11374                       if ( id == 0 )
11375                       {
11376                         gref.SetCoord( values[0], values[1], values[2] );
11377                         angleDom[idom] = 0;
11378                       }
11379                       else
11380                       {
11381                         gp_Pnt g( values[0], values[1], values[2] );
11382                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11383                         //MESSAGE("  angle=" << angleDom[idom]);
11384                       }
11385                       break;
11386                     }
11387                   }
11388                 }
11389                 map<double, int> sortedDom; // sort domains by angle
11390                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11391                   sortedDom[ia->second] = ia->first;
11392                 vector<int> vnodes;
11393                 vector<int> vdom;
11394                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11395                 {
11396                   vdom.push_back(ib->second);
11397                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11398                 }
11399                 for (int ino = 0; ino < nbNodes; ino++)
11400                   vnodes.push_back(nodes[ino]);
11401                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11402               }
11403             }
11404           }
11405         }
11406       }
11407     }
11408   }
11409
11410   // --- iterate on shared faces (volumes to modify, face to extrude)
11411   //     get node id's of the face (id SMDS = id VTK)
11412   //     create flat element with old and new nodes if requested
11413
11414   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11415   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11416
11417   std::map<int, std::map<long,int> > nodeQuadDomains;
11418   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11419
11420   //MESSAGE(".. Creation of elements: simple junction");
11421   if (createJointElems)
11422   {
11423     string joints2DName = "joints2D";
11424     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11425     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11426     string joints3DName = "joints3D";
11427     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11428     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11429
11430     itface = faceDomains.begin();
11431     for (; itface != faceDomains.end(); ++itface)
11432     {
11433       DownIdType face = itface->first;
11434       std::set<int> oldNodes;
11435       std::set<int>::iterator itn;
11436       oldNodes.clear();
11437       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11438
11439       std::map<int, int> domvol = itface->second;
11440       std::map<int, int>::iterator itdom = domvol.begin();
11441       int dom1 = itdom->first;
11442       int vtkVolId = itdom->second;
11443       itdom++;
11444       int dom2 = itdom->first;
11445       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11446                                                        nodeQuadDomains);
11447       stringstream grpname;
11448       grpname << "j_";
11449       if (dom1 < dom2)
11450         grpname << dom1 << "_" << dom2;
11451       else
11452         grpname << dom2 << "_" << dom1;
11453       string namegrp = grpname.str();
11454       if (!mapOfJunctionGroups.count(namegrp))
11455         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11456       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11457       if (sgrp)
11458         sgrp->Add(vol->GetID());
11459       if (vol->GetType() == SMDSAbs_Volume)
11460         joints3DGrp->Add(vol->GetID());
11461       else if (vol->GetType() == SMDSAbs_Face)
11462         joints2DGrp->Add(vol->GetID());
11463     }
11464   }
11465
11466   // --- create volumes on multiple domain intersection if requested
11467   //     iterate on mutipleNodesToFace
11468   //     iterate on edgesMultiDomains
11469
11470   //MESSAGE(".. Creation of elements: multiple junction");
11471   if (createJointElems)
11472   {
11473     // --- iterate on mutipleNodesToFace
11474
11475     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11476     for (; itn != mutipleNodesToFace.end(); ++itn)
11477     {
11478       int node = itn->first;
11479       vector<int> orderDom = itn->second;
11480       vector<vtkIdType> orderedNodes;
11481       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11482         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11483       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11484
11485       stringstream grpname;
11486       grpname << "m2j_";
11487       grpname << 0 << "_" << 0;
11488       string namegrp = grpname.str();
11489       if (!mapOfJunctionGroups.count(namegrp))
11490         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11491       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11492       if (sgrp)
11493         sgrp->Add(face->GetID());
11494     }
11495
11496     // --- iterate on edgesMultiDomains
11497
11498     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11499     for (; ite != edgesMultiDomains.end(); ++ite)
11500     {
11501       vector<int> nodes = ite->first;
11502       vector<int> orderDom = ite->second;
11503       vector<vtkIdType> orderedNodes;
11504       if (nodes.size() == 2)
11505       {
11506         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11507         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11508           if ( orderDom.size() == 3 )
11509             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11510               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11511           else
11512             for (int idom = orderDom.size()-1; idom >=0; idom--)
11513               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11514         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11515
11516         string namegrp = "jointsMultiples";
11517         if (!mapOfJunctionGroups.count(namegrp))
11518           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11519         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11520         if (sgrp)
11521           sgrp->Add(vol->GetID());
11522       }
11523       else
11524       {
11525         //INFOS("Quadratic multiple joints not implemented");
11526         // TODO quadratic nodes
11527       }
11528     }
11529   }
11530
11531   // --- list the explicit faces and edges of the mesh that need to be modified,
11532   //     i.e. faces and edges built with one or more duplicated nodes.
11533   //     associate these faces or edges to their corresponding domain.
11534   //     only the first domain found is kept when a face or edge is shared
11535
11536   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11537   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11538   faceOrEdgeDom.clear();
11539   feDom.clear();
11540
11541   //MESSAGE(".. Modification of elements");
11542   for (int idomain = idom0; idomain < nbDomains; idomain++)
11543   {
11544     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11545     for (; itnod != nodeDomains.end(); ++itnod)
11546     {
11547       int oldId = itnod->first;
11548       //MESSAGE("     node " << oldId);
11549       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11550       for (int i = 0; i < l.ncells; i++)
11551       {
11552         int vtkId = l.cells[i];
11553         int vtkType = grid->GetCellType(vtkId);
11554         int downId = grid->CellIdToDownId(vtkId);
11555         if (downId < 0)
11556           continue; // new cells: not to be modified
11557         DownIdType aCell(downId, vtkType);
11558         int volParents[1000];
11559         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11560         for (int j = 0; j < nbvol; j++)
11561           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11562             if (!feDom.count(vtkId))
11563             {
11564               feDom[vtkId] = idomain;
11565               faceOrEdgeDom[aCell] = emptyMap;
11566               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11567               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11568               //        << " type " << vtkType << " downId " << downId);
11569             }
11570       }
11571     }
11572   }
11573
11574   // --- iterate on shared faces (volumes to modify, face to extrude)
11575   //     get node id's of the face
11576   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11577
11578   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11579   for (int m=0; m<3; m++)
11580   {
11581     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11582     itface = (*amap).begin();
11583     for (; itface != (*amap).end(); ++itface)
11584     {
11585       DownIdType face = itface->first;
11586       std::set<int> oldNodes;
11587       std::set<int>::iterator itn;
11588       oldNodes.clear();
11589       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11590       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11591       std::map<int, int> localClonedNodeIds;
11592
11593       std::map<int, int> domvol = itface->second;
11594       std::map<int, int>::iterator itdom = domvol.begin();
11595       for (; itdom != domvol.end(); ++itdom)
11596       {
11597         int idom = itdom->first;
11598         int vtkVolId = itdom->second;
11599         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11600         localClonedNodeIds.clear();
11601         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11602         {
11603           int oldId = *itn;
11604           if (nodeDomains[oldId].count(idom))
11605           {
11606             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11607             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11608           }
11609         }
11610         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11611       }
11612     }
11613   }
11614
11615   // Remove empty groups (issue 0022812)
11616   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11617   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11618   {
11619     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11620       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11621   }
11622
11623   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11624   grid->DeleteLinks();
11625
11626   CHRONOSTOP(50);
11627   counters::stats();
11628   return true;
11629 }
11630
11631 /*!
11632  * \brief Double nodes on some external faces and create flat elements.
11633  * Flat elements are mainly used by some types of mechanic calculations.
11634  *
11635  * Each group of the list must be constituted of faces.
11636  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11637  * @param theElems - list of groups of faces, where a group of faces is a set of
11638  * SMDS_MeshElements sorted by Id.
11639  * @return TRUE if operation has been completed successfully, FALSE otherwise
11640  */
11641 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11642 {
11643   // MESSAGE("-------------------------------------------------");
11644   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11645   // MESSAGE("-------------------------------------------------");
11646
11647   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11648
11649   // --- For each group of faces
11650   //     duplicate the nodes, create a flat element based on the face
11651   //     replace the nodes of the faces by their clones
11652
11653   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11654   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11655   clonedNodes.clear();
11656   intermediateNodes.clear();
11657   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11658   mapOfJunctionGroups.clear();
11659
11660   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11661   {
11662     const TIDSortedElemSet&           domain = theElems[idom];
11663     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11664     for ( ; elemItr != domain.end(); ++elemItr )
11665     {
11666       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11667       if (!aFace)
11668         continue;
11669       // MESSAGE("aFace=" << aFace->GetID());
11670       bool isQuad = aFace->IsQuadratic();
11671       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11672
11673       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11674
11675       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11676       while (nodeIt->more())
11677       {
11678         const SMDS_MeshNode* node = nodeIt->next();
11679         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11680         if (isMedium)
11681           ln2.push_back(node);
11682         else
11683           ln0.push_back(node);
11684
11685         const SMDS_MeshNode* clone = 0;
11686         if (!clonedNodes.count(node))
11687         {
11688           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11689           copyPosition( node, clone );
11690           clonedNodes[node] = clone;
11691         }
11692         else
11693           clone = clonedNodes[node];
11694
11695         if (isMedium)
11696           ln3.push_back(clone);
11697         else
11698           ln1.push_back(clone);
11699
11700         const SMDS_MeshNode* inter = 0;
11701         if (isQuad && (!isMedium))
11702         {
11703           if (!intermediateNodes.count(node))
11704           {
11705             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11706             copyPosition( node, inter );
11707             intermediateNodes[node] = inter;
11708           }
11709           else
11710             inter = intermediateNodes[node];
11711           ln4.push_back(inter);
11712         }
11713       }
11714
11715       // --- extrude the face
11716
11717       vector<const SMDS_MeshNode*> ln;
11718       SMDS_MeshVolume* vol = 0;
11719       vtkIdType aType = aFace->GetVtkType();
11720       switch (aType)
11721       {
11722       case VTK_TRIANGLE:
11723         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11724         // MESSAGE("vol prism " << vol->GetID());
11725         ln.push_back(ln1[0]);
11726         ln.push_back(ln1[1]);
11727         ln.push_back(ln1[2]);
11728         break;
11729       case VTK_QUAD:
11730         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11731         // MESSAGE("vol hexa " << vol->GetID());
11732         ln.push_back(ln1[0]);
11733         ln.push_back(ln1[1]);
11734         ln.push_back(ln1[2]);
11735         ln.push_back(ln1[3]);
11736         break;
11737       case VTK_QUADRATIC_TRIANGLE:
11738         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11739                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11740         // MESSAGE("vol quad prism " << vol->GetID());
11741         ln.push_back(ln1[0]);
11742         ln.push_back(ln1[1]);
11743         ln.push_back(ln1[2]);
11744         ln.push_back(ln3[0]);
11745         ln.push_back(ln3[1]);
11746         ln.push_back(ln3[2]);
11747         break;
11748       case VTK_QUADRATIC_QUAD:
11749         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11750         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11751         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11752         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11753                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11754                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11755         // MESSAGE("vol quad hexa " << vol->GetID());
11756         ln.push_back(ln1[0]);
11757         ln.push_back(ln1[1]);
11758         ln.push_back(ln1[2]);
11759         ln.push_back(ln1[3]);
11760         ln.push_back(ln3[0]);
11761         ln.push_back(ln3[1]);
11762         ln.push_back(ln3[2]);
11763         ln.push_back(ln3[3]);
11764         break;
11765       case VTK_POLYGON:
11766         break;
11767       default:
11768         break;
11769       }
11770
11771       if (vol)
11772       {
11773         stringstream grpname;
11774         grpname << "jf_";
11775         grpname << idom;
11776         string namegrp = grpname.str();
11777         if (!mapOfJunctionGroups.count(namegrp))
11778           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11779         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11780         if (sgrp)
11781           sgrp->Add(vol->GetID());
11782       }
11783
11784       // --- modify the face
11785
11786       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11787     }
11788   }
11789   return true;
11790 }
11791
11792 /*!
11793  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11794  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11795  *  groups of faces to remove inside the object, (idem edges).
11796  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11797  */
11798 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11799                                       const TopoDS_Shape&             theShape,
11800                                       SMESH_NodeSearcher*             theNodeSearcher,
11801                                       const char*                     groupName,
11802                                       std::vector<double>&            nodesCoords,
11803                                       std::vector<std::vector<int> >& listOfListOfNodes)
11804 {
11805   // MESSAGE("--------------------------------");
11806   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11807   // MESSAGE("--------------------------------");
11808
11809   // --- zone of volumes to remove is given :
11810   //     1 either by a geom shape (one or more vertices) and a radius,
11811   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11812   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11813   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11814   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11815   //     defined by it's name.
11816
11817   SMESHDS_GroupBase* groupDS = 0;
11818   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11819   while ( groupIt->more() )
11820   {
11821     groupDS = 0;
11822     SMESH_Group * group = groupIt->next();
11823     if ( !group ) continue;
11824     groupDS = group->GetGroupDS();
11825     if ( !groupDS || groupDS->IsEmpty() ) continue;
11826     std::string grpName = group->GetName();
11827     //MESSAGE("grpName=" << grpName);
11828     if (grpName == groupName)
11829       break;
11830     else
11831       groupDS = 0;
11832   }
11833
11834   bool isNodeGroup = false;
11835   bool isNodeCoords = false;
11836   if (groupDS)
11837   {
11838     if (groupDS->GetType() != SMDSAbs_Node)
11839       return;
11840     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11841   }
11842
11843   if (nodesCoords.size() > 0)
11844     isNodeCoords = true; // a list o nodes given by their coordinates
11845   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11846
11847   // --- define groups to build
11848
11849   // --- group of SMDS volumes
11850   string grpvName = groupName;
11851   grpvName += "_vol";
11852   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11853   if (!grp)
11854   {
11855     MESSAGE("group not created " << grpvName);
11856     return;
11857   }
11858   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11859
11860   // --- group of SMDS faces on the skin
11861   string grpsName = groupName;
11862   grpsName += "_skin";
11863   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11864   if (!grps)
11865   {
11866     MESSAGE("group not created " << grpsName);
11867     return;
11868   }
11869   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11870
11871   // --- group of SMDS faces internal (several shapes)
11872   string grpiName = groupName;
11873   grpiName += "_internalFaces";
11874   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11875   if (!grpi)
11876   {
11877     MESSAGE("group not created " << grpiName);
11878     return;
11879   }
11880   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11881
11882   // --- group of SMDS faces internal (several shapes)
11883   string grpeiName = groupName;
11884   grpeiName += "_internalEdges";
11885   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11886   if (!grpei)
11887   {
11888     MESSAGE("group not created " << grpeiName);
11889     return;
11890   }
11891   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11892
11893   // --- build downward connectivity
11894
11895   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11896   meshDS->BuildDownWardConnectivity(true);
11897   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11898
11899   // --- set of volumes detected inside
11900
11901   std::set<int> setOfInsideVol;
11902   std::set<int> setOfVolToCheck;
11903
11904   std::vector<gp_Pnt> gpnts;
11905   gpnts.clear();
11906
11907   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11908   {
11909     //MESSAGE("group of nodes provided");
11910     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11911     while ( elemIt->more() )
11912     {
11913       const SMDS_MeshElement* elem = elemIt->next();
11914       if (!elem)
11915         continue;
11916       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11917       if (!node)
11918         continue;
11919       SMDS_MeshElement* vol = 0;
11920       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11921       while (volItr->more())
11922       {
11923         vol = (SMDS_MeshElement*)volItr->next();
11924         setOfInsideVol.insert(vol->GetVtkID());
11925         sgrp->Add(vol->GetID());
11926       }
11927     }
11928   }
11929   else if (isNodeCoords)
11930   {
11931     //MESSAGE("list of nodes coordinates provided");
11932     size_t i = 0;
11933     int k = 0;
11934     while ( i < nodesCoords.size()-2 )
11935     {
11936       double x = nodesCoords[i++];
11937       double y = nodesCoords[i++];
11938       double z = nodesCoords[i++];
11939       gp_Pnt p = gp_Pnt(x, y ,z);
11940       gpnts.push_back(p);
11941       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11942       k++;
11943     }
11944   }
11945   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11946   {
11947     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11948     TopTools_IndexedMapOfShape vertexMap;
11949     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11950     gp_Pnt p = gp_Pnt(0,0,0);
11951     if (vertexMap.Extent() < 1)
11952       return;
11953
11954     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11955     {
11956       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11957       p = BRep_Tool::Pnt(vertex);
11958       gpnts.push_back(p);
11959       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11960     }
11961   }
11962
11963   if (gpnts.size() > 0)
11964   {
11965     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11966     //MESSAGE("startNode->nodeId " << nodeId);
11967
11968     double radius2 = radius*radius;
11969     //MESSAGE("radius2 " << radius2);
11970
11971     // --- volumes on start node
11972
11973     setOfVolToCheck.clear();
11974     SMDS_MeshElement* startVol = 0;
11975     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11976     while (volItr->more())
11977     {
11978       startVol = (SMDS_MeshElement*)volItr->next();
11979       setOfVolToCheck.insert(startVol->GetVtkID());
11980     }
11981     if (setOfVolToCheck.empty())
11982     {
11983       MESSAGE("No volumes found");
11984       return;
11985     }
11986
11987     // --- starting with central volumes then their neighbors, check if they are inside
11988     //     or outside the domain, until no more new neighbor volume is inside.
11989     //     Fill the group of inside volumes
11990
11991     std::map<int, double> mapOfNodeDistance2;
11992     mapOfNodeDistance2.clear();
11993     std::set<int> setOfOutsideVol;
11994     while (!setOfVolToCheck.empty())
11995     {
11996       std::set<int>::iterator it = setOfVolToCheck.begin();
11997       int vtkId = *it;
11998       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11999       bool volInside = false;
12000       vtkIdType npts = 0;
12001       vtkIdType* pts = 0;
12002       grid->GetCellPoints(vtkId, npts, pts);
12003       for (int i=0; i<npts; i++)
12004       {
12005         double distance2 = 0;
12006         if (mapOfNodeDistance2.count(pts[i]))
12007         {
12008           distance2 = mapOfNodeDistance2[pts[i]];
12009           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12010         }
12011         else
12012         {
12013           double *coords = grid->GetPoint(pts[i]);
12014           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12015           distance2 = 1.E40;
12016           for ( size_t j = 0; j < gpnts.size(); j++ )
12017           {
12018             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12019             if (d2 < distance2)
12020             {
12021               distance2 = d2;
12022               if (distance2 < radius2)
12023                 break;
12024             }
12025           }
12026           mapOfNodeDistance2[pts[i]] = distance2;
12027           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12028         }
12029         if (distance2 < radius2)
12030         {
12031           volInside = true; // one or more nodes inside the domain
12032           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12033           break;
12034         }
12035       }
12036       if (volInside)
12037       {
12038         setOfInsideVol.insert(vtkId);
12039         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12040         int neighborsVtkIds[NBMAXNEIGHBORS];
12041         int downIds[NBMAXNEIGHBORS];
12042         unsigned char downTypes[NBMAXNEIGHBORS];
12043         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12044         for (int n = 0; n < nbNeighbors; n++)
12045           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12046             setOfVolToCheck.insert(neighborsVtkIds[n]);
12047       }
12048       else
12049       {
12050         setOfOutsideVol.insert(vtkId);
12051         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12052       }
12053       setOfVolToCheck.erase(vtkId);
12054     }
12055   }
12056
12057   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12058   //     If yes, add the volume to the inside set
12059
12060   bool addedInside = true;
12061   std::set<int> setOfVolToReCheck;
12062   while (addedInside)
12063   {
12064     //MESSAGE(" --------------------------- re check");
12065     addedInside = false;
12066     std::set<int>::iterator itv = setOfInsideVol.begin();
12067     for (; itv != setOfInsideVol.end(); ++itv)
12068     {
12069       int vtkId = *itv;
12070       int neighborsVtkIds[NBMAXNEIGHBORS];
12071       int downIds[NBMAXNEIGHBORS];
12072       unsigned char downTypes[NBMAXNEIGHBORS];
12073       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12074       for (int n = 0; n < nbNeighbors; n++)
12075         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12076           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12077     }
12078     setOfVolToCheck = setOfVolToReCheck;
12079     setOfVolToReCheck.clear();
12080     while  (!setOfVolToCheck.empty())
12081     {
12082       std::set<int>::iterator it = setOfVolToCheck.begin();
12083       int vtkId = *it;
12084       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12085       {
12086         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12087         int countInside = 0;
12088         int neighborsVtkIds[NBMAXNEIGHBORS];
12089         int downIds[NBMAXNEIGHBORS];
12090         unsigned char downTypes[NBMAXNEIGHBORS];
12091         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12092         for (int n = 0; n < nbNeighbors; n++)
12093           if (setOfInsideVol.count(neighborsVtkIds[n]))
12094             countInside++;
12095         //MESSAGE("countInside " << countInside);
12096         if (countInside > 1)
12097         {
12098           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12099           setOfInsideVol.insert(vtkId);
12100           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12101           addedInside = true;
12102         }
12103         else
12104           setOfVolToReCheck.insert(vtkId);
12105       }
12106       setOfVolToCheck.erase(vtkId);
12107     }
12108   }
12109
12110   // --- map of Downward faces at the boundary, inside the global volume
12111   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12112   //     fill group of SMDS faces inside the volume (when several volume shapes)
12113   //     fill group of SMDS faces on the skin of the global volume (if skin)
12114
12115   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12116   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12117   std::set<int>::iterator it = setOfInsideVol.begin();
12118   for (; it != setOfInsideVol.end(); ++it)
12119   {
12120     int vtkId = *it;
12121     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12122     int neighborsVtkIds[NBMAXNEIGHBORS];
12123     int downIds[NBMAXNEIGHBORS];
12124     unsigned char downTypes[NBMAXNEIGHBORS];
12125     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12126     for (int n = 0; n < nbNeighbors; n++)
12127     {
12128       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12129       if (neighborDim == 3)
12130       {
12131         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12132         {
12133           DownIdType face(downIds[n], downTypes[n]);
12134           boundaryFaces[face] = vtkId;
12135         }
12136         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12137         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12138         if (vtkFaceId >= 0)
12139         {
12140           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12141           // find also the smds edges on this face
12142           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12143           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12144           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12145           for (int i = 0; i < nbEdges; i++)
12146           {
12147             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12148             if (vtkEdgeId >= 0)
12149               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12150           }
12151         }
12152       }
12153       else if (neighborDim == 2) // skin of the volume
12154       {
12155         DownIdType face(downIds[n], downTypes[n]);
12156         skinFaces[face] = vtkId;
12157         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12158         if (vtkFaceId >= 0)
12159           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12160       }
12161     }
12162   }
12163
12164   // --- identify the edges constituting the wire of each subshape on the skin
12165   //     define polylines with the nodes of edges, equivalent to wires
12166   //     project polylines on subshapes, and partition, to get geom faces
12167
12168   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12169   std::set<int> emptySet;
12170   emptySet.clear();
12171   std::set<int> shapeIds;
12172
12173   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12174   while (itelem->more())
12175   {
12176     const SMDS_MeshElement *elem = itelem->next();
12177     int shapeId = elem->getshapeId();
12178     int   vtkId = elem->GetVtkID();
12179     if (!shapeIdToVtkIdSet.count(shapeId))
12180     {
12181       shapeIdToVtkIdSet[shapeId] = emptySet;
12182       shapeIds.insert(shapeId);
12183     }
12184     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12185   }
12186
12187   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12188   std::set<DownIdType, DownIdCompare> emptyEdges;
12189   emptyEdges.clear();
12190
12191   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12192   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12193   {
12194     int shapeId = itShape->first;
12195     //MESSAGE(" --- Shape ID --- "<< shapeId);
12196     shapeIdToEdges[shapeId] = emptyEdges;
12197
12198     std::vector<int> nodesEdges;
12199
12200     std::set<int>::iterator its = itShape->second.begin();
12201     for (; its != itShape->second.end(); ++its)
12202     {
12203       int vtkId = *its;
12204       //MESSAGE("     " << vtkId);
12205       int neighborsVtkIds[NBMAXNEIGHBORS];
12206       int downIds[NBMAXNEIGHBORS];
12207       unsigned char downTypes[NBMAXNEIGHBORS];
12208       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12209       for (int n = 0; n < nbNeighbors; n++)
12210       {
12211         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12212           continue;
12213         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12214         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12215         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12216         {
12217           DownIdType edge(downIds[n], downTypes[n]);
12218           if (!shapeIdToEdges[shapeId].count(edge))
12219           {
12220             shapeIdToEdges[shapeId].insert(edge);
12221             int vtkNodeId[3];
12222             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12223             nodesEdges.push_back(vtkNodeId[0]);
12224             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12225             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12226           }
12227         }
12228       }
12229     }
12230
12231     std::list<int> order;
12232     order.clear();
12233     if (nodesEdges.size() > 0)
12234     {
12235       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12236       nodesEdges[0] = -1;
12237       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12238       nodesEdges[1] = -1; // do not reuse this edge
12239       bool found = true;
12240       while (found)
12241       {
12242         int nodeTofind = order.back(); // try first to push back
12243         int i = 0;
12244         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12245           if (nodesEdges[i] == nodeTofind)
12246             break;
12247         if ( i == (int) nodesEdges.size() )
12248           found = false; // no follower found on back
12249         else
12250         {
12251           if (i%2) // odd ==> use the previous one
12252             if (nodesEdges[i-1] < 0)
12253               found = false;
12254             else
12255             {
12256               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12257               nodesEdges[i-1] = -1;
12258             }
12259           else // even ==> use the next one
12260             if (nodesEdges[i+1] < 0)
12261               found = false;
12262             else
12263             {
12264               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12265               nodesEdges[i+1] = -1;
12266             }
12267         }
12268         if (found)
12269           continue;
12270         // try to push front
12271         found = true;
12272         nodeTofind = order.front(); // try to push front
12273         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12274           if ( nodesEdges[i] == nodeTofind )
12275             break;
12276         if ( i == (int)nodesEdges.size() )
12277         {
12278           found = false; // no predecessor found on front
12279           continue;
12280         }
12281         if (i%2) // odd ==> use the previous one
12282           if (nodesEdges[i-1] < 0)
12283             found = false;
12284           else
12285           {
12286             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12287             nodesEdges[i-1] = -1;
12288           }
12289         else // even ==> use the next one
12290           if (nodesEdges[i+1] < 0)
12291             found = false;
12292           else
12293           {
12294             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12295             nodesEdges[i+1] = -1;
12296           }
12297       }
12298     }
12299
12300
12301     std::vector<int> nodes;
12302     nodes.push_back(shapeId);
12303     std::list<int>::iterator itl = order.begin();
12304     for (; itl != order.end(); itl++)
12305     {
12306       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12307       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12308     }
12309     listOfListOfNodes.push_back(nodes);
12310   }
12311
12312   //     partition geom faces with blocFissure
12313   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12314   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12315
12316   return;
12317 }
12318
12319
12320 //================================================================================
12321 /*!
12322  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12323  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12324  * \return TRUE if operation has been completed successfully, FALSE otherwise
12325  */
12326 //================================================================================
12327
12328 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12329 {
12330   // iterates on volume elements and detect all free faces on them
12331   SMESHDS_Mesh* aMesh = GetMeshDS();
12332   if (!aMesh)
12333     return false;
12334
12335   ElemFeatures faceType( SMDSAbs_Face );
12336   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12337   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12338   while(vIt->more())
12339   {
12340     const SMDS_MeshVolume* volume = vIt->next();
12341     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12342     vTool.SetExternalNormal();
12343     const int iQuad = volume->IsQuadratic();
12344     faceType.SetQuad( iQuad );
12345     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12346     {
12347       if (!vTool.IsFreeFace(iface))
12348         continue;
12349       nbFree++;
12350       vector<const SMDS_MeshNode *> nodes;
12351       int nbFaceNodes = vTool.NbFaceNodes(iface);
12352       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12353       int inode = 0;
12354       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12355         nodes.push_back(faceNodes[inode]);
12356
12357       if (iQuad) // add medium nodes
12358       {
12359         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12360           nodes.push_back(faceNodes[inode]);
12361         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12362           nodes.push_back(faceNodes[8]);
12363       }
12364       // add new face based on volume nodes
12365       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12366       {
12367         nbExisted++; // face already exists
12368       }
12369       else
12370       {
12371         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12372         nbCreated++;
12373       }
12374     }
12375   }
12376   return ( nbFree == ( nbExisted + nbCreated ));
12377 }
12378
12379 namespace
12380 {
12381   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12382   {
12383     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12384       return n;
12385     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12386   }
12387 }
12388 //================================================================================
12389 /*!
12390  * \brief Creates missing boundary elements
12391  *  \param elements - elements whose boundary is to be checked
12392  *  \param dimension - defines type of boundary elements to create
12393  *  \param group - a group to store created boundary elements in
12394  *  \param targetMesh - a mesh to store created boundary elements in
12395  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12396  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12397  *                                boundary elements will be copied into the targetMesh
12398  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12399  *                                boundary elements will be added into the new group
12400  *  \param aroundElements - if true, elements will be created on boundary of given
12401  *                          elements else, on boundary of the whole mesh.
12402  * \return nb of added boundary elements
12403  */
12404 //================================================================================
12405
12406 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12407                                        Bnd_Dimension           dimension,
12408                                        SMESH_Group*            group/*=0*/,
12409                                        SMESH_Mesh*             targetMesh/*=0*/,
12410                                        bool                    toCopyElements/*=false*/,
12411                                        bool                    toCopyExistingBoundary/*=false*/,
12412                                        bool                    toAddExistingBondary/*= false*/,
12413                                        bool                    aroundElements/*= false*/)
12414 {
12415   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12416   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12417   // hope that all elements are of the same type, do not check them all
12418   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12419     throw SALOME_Exception(LOCALIZED("wrong element type"));
12420
12421   if ( !targetMesh )
12422     toCopyElements = toCopyExistingBoundary = false;
12423
12424   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12425   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12426   int nbAddedBnd = 0;
12427
12428   // editor adding present bnd elements and optionally holding elements to add to the group
12429   SMESH_MeshEditor* presentEditor;
12430   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12431   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12432
12433   SMESH_MesherHelper helper( *myMesh );
12434   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12435   SMDS_VolumeTool vTool;
12436   TIDSortedElemSet avoidSet;
12437   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12438   size_t inode;
12439
12440   typedef vector<const SMDS_MeshNode*> TConnectivity;
12441   TConnectivity tgtNodes;
12442   ElemFeatures elemKind( missType ), elemToCopy;
12443
12444   vector<const SMDS_MeshElement*> presentBndElems;
12445   vector<TConnectivity>           missingBndElems;
12446   vector<int>                     freeFacets;
12447   TConnectivity nodes, elemNodes;
12448
12449   SMDS_ElemIteratorPtr eIt;
12450   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12451   else                  eIt = SMESHUtils::elemSetIterator( elements );
12452
12453   while ( eIt->more() )
12454   {
12455     const SMDS_MeshElement* elem = eIt->next();
12456     const int              iQuad = elem->IsQuadratic();
12457     elemKind.SetQuad( iQuad );
12458
12459     // ------------------------------------------------------------------------------------
12460     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12461     // ------------------------------------------------------------------------------------
12462     presentBndElems.clear();
12463     missingBndElems.clear();
12464     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12465     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12466     {
12467       const SMDS_MeshElement* otherVol = 0;
12468       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12469       {
12470         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12471              ( !aroundElements || elements.count( otherVol )))
12472           continue;
12473         freeFacets.push_back( iface );
12474       }
12475       if ( missType == SMDSAbs_Face )
12476         vTool.SetExternalNormal();
12477       for ( size_t i = 0; i < freeFacets.size(); ++i )
12478       {
12479         int                iface = freeFacets[i];
12480         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12481         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12482         if ( missType == SMDSAbs_Edge ) // boundary edges
12483         {
12484           nodes.resize( 2+iQuad );
12485           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12486           {
12487             for ( size_t j = 0; j < nodes.size(); ++j )
12488               nodes[ j ] = nn[ i+j ];
12489             if ( const SMDS_MeshElement* edge =
12490                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12491               presentBndElems.push_back( edge );
12492             else
12493               missingBndElems.push_back( nodes );
12494           }
12495         }
12496         else // boundary face
12497         {
12498           nodes.clear();
12499           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12500             nodes.push_back( nn[inode] ); // add corner nodes
12501           if (iQuad)
12502             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12503               nodes.push_back( nn[inode] ); // add medium nodes
12504           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12505           if ( iCenter > 0 )
12506             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12507
12508           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12509                                                                SMDSAbs_Face, /*noMedium=*/false ))
12510             presentBndElems.push_back( f );
12511           else
12512             missingBndElems.push_back( nodes );
12513
12514           if ( targetMesh != myMesh )
12515           {
12516             // add 1D elements on face boundary to be added to a new mesh
12517             const SMDS_MeshElement* edge;
12518             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12519             {
12520               if ( iQuad )
12521                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12522               else
12523                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12524               if ( edge && avoidSet.insert( edge ).second )
12525                 presentBndElems.push_back( edge );
12526             }
12527           }
12528         }
12529       }
12530     }
12531     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12532     {
12533       avoidSet.clear(), avoidSet.insert( elem );
12534       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12535                         SMDS_MeshElement::iterator() );
12536       elemNodes.push_back( elemNodes[0] );
12537       nodes.resize( 2 + iQuad );
12538       const int nbLinks = elem->NbCornerNodes();
12539       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12540       {
12541         nodes[0] = elemNodes[iN];
12542         nodes[1] = elemNodes[iN+1+iQuad];
12543         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12544           continue; // not free link
12545
12546         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12547         if ( const SMDS_MeshElement* edge =
12548              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12549           presentBndElems.push_back( edge );
12550         else
12551           missingBndElems.push_back( nodes );
12552       }
12553     }
12554
12555     // ---------------------------------
12556     // 2. Add missing boundary elements
12557     // ---------------------------------
12558     if ( targetMesh != myMesh )
12559       // instead of making a map of nodes in this mesh and targetMesh,
12560       // we create nodes with same IDs.
12561       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12562       {
12563         TConnectivity& srcNodes = missingBndElems[i];
12564         tgtNodes.resize( srcNodes.size() );
12565         for ( inode = 0; inode < srcNodes.size(); ++inode )
12566           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12567         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12568                                                                    missType,
12569                                                                    /*noMedium=*/false))
12570           continue;
12571         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12572         ++nbAddedBnd;
12573       }
12574     else
12575       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12576       {
12577         TConnectivity& nodes = missingBndElems[ i ];
12578         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12579                                                                    missType,
12580                                                                    /*noMedium=*/false))
12581           continue;
12582         SMDS_MeshElement* newElem =
12583           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12584         nbAddedBnd += bool( newElem );
12585
12586         // try to set a new element to a shape
12587         if ( myMesh->HasShapeToMesh() )
12588         {
12589           bool ok = true;
12590           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12591           const size_t nbN = nodes.size() / (iQuad+1 );
12592           for ( inode = 0; inode < nbN && ok; ++inode )
12593           {
12594             pair<int, TopAbs_ShapeEnum> i_stype =
12595               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12596             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12597               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12598           }
12599           if ( ok && mediumShapes.size() > 1 )
12600           {
12601             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12602             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12603             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12604             {
12605               if (( ok = ( stype_i->first != stype_i_0.first )))
12606                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12607                                         aMesh->IndexToShape( stype_i_0.second ));
12608             }
12609           }
12610           if ( ok && mediumShapes.begin()->first == missShapeType )
12611             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12612         }
12613       }
12614
12615     // ----------------------------------
12616     // 3. Copy present boundary elements
12617     // ----------------------------------
12618     if ( toCopyExistingBoundary )
12619       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12620       {
12621         const SMDS_MeshElement* e = presentBndElems[i];
12622         tgtNodes.resize( e->NbNodes() );
12623         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12624           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12625         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12626       }
12627     else // store present elements to add them to a group
12628       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12629       {
12630         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12631       }
12632
12633   } // loop on given elements
12634
12635   // ---------------------------------------------
12636   // 4. Fill group with boundary elements
12637   // ---------------------------------------------
12638   if ( group )
12639   {
12640     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12641       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12642         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12643   }
12644   tgtEditor.myLastCreatedElems.clear();
12645   tgtEditor2.myLastCreatedElems.clear();
12646
12647   // -----------------------
12648   // 5. Copy given elements
12649   // -----------------------
12650   if ( toCopyElements && targetMesh != myMesh )
12651   {
12652     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12653     else                  eIt = SMESHUtils::elemSetIterator( elements );
12654     while (eIt->more())
12655     {
12656       const SMDS_MeshElement* elem = eIt->next();
12657       tgtNodes.resize( elem->NbNodes() );
12658       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12659         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12660       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12661
12662       tgtEditor.myLastCreatedElems.clear();
12663     }
12664   }
12665   return nbAddedBnd;
12666 }
12667
12668 //================================================================================
12669 /*!
12670  * \brief Copy node position and set \a to node on the same geometry
12671  */
12672 //================================================================================
12673
12674 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12675                                      const SMDS_MeshNode* to )
12676 {
12677   if ( !from || !to ) return;
12678
12679   SMDS_PositionPtr pos = from->GetPosition();
12680   if ( !pos || from->getshapeId() < 1 ) return;
12681
12682   switch ( pos->GetTypeOfPosition() )
12683   {
12684   case SMDS_TOP_3DSPACE: break;
12685
12686   case SMDS_TOP_FACE:
12687   {
12688     SMDS_FacePositionPtr fPos = pos;
12689     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12690                                 fPos->GetUParameter(), fPos->GetVParameter() );
12691     break;
12692   }
12693   case SMDS_TOP_EDGE:
12694   {
12695     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12696     SMDS_EdgePositionPtr ePos = pos;
12697     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12698     break;
12699   }
12700   case SMDS_TOP_VERTEX:
12701   {
12702     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12703     break;
12704   }
12705   case SMDS_TOP_UNSPEC:
12706   default:;
12707   }
12708 }