Salome HOME
23627: [IMACS] ASERIS: project point to the mesh and create a slot
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
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 : nbEdgeConnectivity
611 //purpose  : return number of the edges connected with the theNode.
612 //           if theEdges has connections with the other type of the
613 //           elements, return -1
614 //=======================================================================
615
616 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
617 {
618   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
619   // int nb=0;
620   // while(elemIt->more()) {
621   //   elemIt->next();
622   //   nb++;
623   // }
624   // return nb;
625   return theNode->NbInverseElements();
626 }
627
628 //=======================================================================
629 //function : getNodesFromTwoTria
630 //purpose  : 
631 //=======================================================================
632
633 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
634                                 const SMDS_MeshElement * theTria2,
635                                 vector< const SMDS_MeshNode*>& N1,
636                                 vector< const SMDS_MeshNode*>& N2)
637 {
638   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
639   if ( N1.size() < 6 ) return false;
640   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
641   if ( N2.size() < 6 ) return false;
642
643   int sames[3] = {-1,-1,-1};
644   int nbsames = 0;
645   int i, j;
646   for(i=0; i<3; i++) {
647     for(j=0; j<3; j++) {
648       if(N1[i]==N2[j]) {
649         sames[i] = j;
650         nbsames++;
651         break;
652       }
653     }
654   }
655   if(nbsames!=2) return false;
656   if(sames[0]>-1) {
657     shiftNodesQuadTria(N1);
658     if(sames[1]>-1) {
659       shiftNodesQuadTria(N1);
660     }
661   }
662   i = sames[0] + sames[1] + sames[2];
663   for(; i<2; i++) {
664     shiftNodesQuadTria(N2);
665   }
666   // now we receive following N1 and N2 (using numeration as in the image below)
667   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
668   // i.e. first nodes from both arrays form a new diagonal
669   return true;
670 }
671
672 //=======================================================================
673 //function : InverseDiag
674 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
675 //           but having other common link.
676 //           Return False if args are improper
677 //=======================================================================
678
679 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
680                                     const SMDS_MeshElement * theTria2 )
681 {
682   ClearLastCreated();
683
684   if ( !theTria1 || !theTria2 ||
685        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
686        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
687        theTria1->GetType() != SMDSAbs_Face ||
688        theTria2->GetType() != SMDSAbs_Face )
689     return false;
690
691   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
692       (theTria2->GetEntityType() == SMDSEntity_Triangle))
693   {
694     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
695     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
696     //    |/ |                                         | \|
697     //  B +--+ 2                                     B +--+ 2
698
699     // put nodes in array and find out indices of the same ones
700     const SMDS_MeshNode* aNodes [6];
701     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
702     int i = 0;
703     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
704     while ( it->more() ) {
705       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
706
707       if ( i > 2 ) // theTria2
708         // find same node of theTria1
709         for ( int j = 0; j < 3; j++ )
710           if ( aNodes[ i ] == aNodes[ j ]) {
711             sameInd[ j ] = i;
712             sameInd[ i ] = j;
713             break;
714           }
715       // next
716       i++;
717       if ( i == 3 ) {
718         if ( it->more() )
719           return false; // theTria1 is not a triangle
720         it = theTria2->nodesIterator();
721       }
722       if ( i == 6 && it->more() )
723         return false; // theTria2 is not a triangle
724     }
725
726     // find indices of 1,2 and of A,B in theTria1
727     int iA = -1, iB = 0, i1 = 0, i2 = 0;
728     for ( i = 0; i < 6; i++ ) {
729       if ( sameInd [ i ] == -1 ) {
730         if ( i < 3 ) i1 = i;
731         else         i2 = i;
732       }
733       else if (i < 3) {
734         if ( iA >= 0) iB = i;
735         else          iA = i;
736       }
737     }
738     // nodes 1 and 2 should not be the same
739     if ( aNodes[ i1 ] == aNodes[ i2 ] )
740       return false;
741
742     // theTria1: A->2
743     aNodes[ iA ] = aNodes[ i2 ];
744     // theTria2: B->1
745     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
746
747     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
748     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
749
750     return true;
751
752   } // end if(F1 && F2)
753
754   // check case of quadratic faces
755   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
756       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
757     return false;
758   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
759       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
760     return false;
761
762   //       5
763   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
764   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
765   //    |   / |
766   //  7 +  +  + 6
767   //    | /9  |
768   //    |/    |
769   //  4 +--+--+ 3
770   //       8
771
772   vector< const SMDS_MeshNode* > N1;
773   vector< const SMDS_MeshNode* > N2;
774   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
775     return false;
776   // now we receive following N1 and N2 (using numeration as above image)
777   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
778   // i.e. first nodes from both arrays determ new diagonal
779
780   vector< const SMDS_MeshNode*> N1new( N1.size() );
781   vector< const SMDS_MeshNode*> N2new( N2.size() );
782   N1new.back() = N1.back(); // central node of biquadratic
783   N2new.back() = N2.back();
784   N1new[0] = N1[0];  N2new[0] = N1[0];
785   N1new[1] = N2[0];  N2new[1] = N1[1];
786   N1new[2] = N2[1];  N2new[2] = N2[0];
787   N1new[3] = N1[4];  N2new[3] = N1[3];
788   N1new[4] = N2[3];  N2new[4] = N2[5];
789   N1new[5] = N1[5];  N2new[5] = N1[4];
790   // change nodes in faces
791   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
792   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
793
794   // move the central node of biquadratic triangle
795   SMESH_MesherHelper helper( *GetMesh() );
796   for ( int is2nd = 0; is2nd < 2; ++is2nd )
797   {
798     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
799     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
800     if ( nodes.size() < 7 )
801       continue;
802     helper.SetSubShape( tria->getshapeId() );
803     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
804     gp_Pnt xyz;
805     if ( F.IsNull() )
806     {
807       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
808               SMESH_NodeXYZ( nodes[4] ) +
809               SMESH_NodeXYZ( nodes[5] )) / 3.;
810     }
811     else
812     {
813       bool checkUV;
814       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
815                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
816                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
817       TopLoc_Location loc;
818       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
819       xyz = S->Value( uv.X(), uv.Y() );
820       xyz.Transform( loc );
821       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
822            nodes[6]->getshapeId() > 0 )
823         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
824     }
825     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
826   }
827   return true;
828 }
829
830 //=======================================================================
831 //function : findTriangles
832 //purpose  : find triangles sharing theNode1-theNode2 link
833 //=======================================================================
834
835 static bool findTriangles(const SMDS_MeshNode *    theNode1,
836                           const SMDS_MeshNode *    theNode2,
837                           const SMDS_MeshElement*& theTria1,
838                           const SMDS_MeshElement*& theTria2)
839 {
840   if ( !theNode1 || !theNode2 ) return false;
841
842   theTria1 = theTria2 = 0;
843
844   set< const SMDS_MeshElement* > emap;
845   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
846   while (it->more()) {
847     const SMDS_MeshElement* elem = it->next();
848     if ( elem->NbCornerNodes() == 3 )
849       emap.insert( elem );
850   }
851   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
852   while (it->more()) {
853     const SMDS_MeshElement* elem = it->next();
854     if ( emap.count( elem )) {
855       if ( !theTria1 )
856       {
857         theTria1 = elem;
858       }
859       else  
860       {
861         theTria2 = elem;
862         // theTria1 must be element with minimum ID
863         if ( theTria2->GetID() < theTria1->GetID() )
864           std::swap( theTria2, theTria1 );
865         return true;
866       }
867     }
868   }
869   return false;
870 }
871
872 //=======================================================================
873 //function : InverseDiag
874 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
875 //           with ones built on the same 4 nodes but having other common link.
876 //           Return false if proper faces not found
877 //=======================================================================
878
879 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
880                                     const SMDS_MeshNode * theNode2)
881 {
882   ClearLastCreated();
883
884   const SMDS_MeshElement *tr1, *tr2;
885   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
886     return false;
887
888   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
889        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
890     return false;
891
892   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
893       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
894
895     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
896     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
897     //    |/ |                                    | \|
898     //  B +--+ 2                                B +--+ 2
899
900     // put nodes in array
901     // and find indices of 1,2 and of A in tr1 and of B in tr2
902     int i, iA1 = 0, i1 = 0;
903     const SMDS_MeshNode* aNodes1 [3];
904     SMDS_ElemIteratorPtr it;
905     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
906       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
907       if ( aNodes1[ i ] == theNode1 )
908         iA1 = i; // node A in tr1
909       else if ( aNodes1[ i ] != theNode2 )
910         i1 = i;  // node 1
911     }
912     int iB2 = 0, i2 = 0;
913     const SMDS_MeshNode* aNodes2 [3];
914     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
915       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916       if ( aNodes2[ i ] == theNode2 )
917         iB2 = i; // node B in tr2
918       else if ( aNodes2[ i ] != theNode1 )
919         i2 = i;  // node 2
920     }
921
922     // nodes 1 and 2 should not be the same
923     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
924       return false;
925
926     // tr1: A->2
927     aNodes1[ iA1 ] = aNodes2[ i2 ];
928     // tr2: B->1
929     aNodes2[ iB2 ] = aNodes1[ i1 ];
930
931     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
932     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
933
934     return true;
935   }
936
937   // check case of quadratic faces
938   return InverseDiag(tr1,tr2);
939 }
940
941 //=======================================================================
942 //function : getQuadrangleNodes
943 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
944 //           fusion of triangles tr1 and tr2 having shared link on
945 //           theNode1 and theNode2
946 //=======================================================================
947
948 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
949                         const SMDS_MeshNode *    theNode1,
950                         const SMDS_MeshNode *    theNode2,
951                         const SMDS_MeshElement * tr1,
952                         const SMDS_MeshElement * tr2 )
953 {
954   if( tr1->NbNodes() != tr2->NbNodes() )
955     return false;
956   // find the 4-th node to insert into tr1
957   const SMDS_MeshNode* n4 = 0;
958   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
959   int i=0;
960   while ( !n4 && i<3 ) {
961     const SMDS_MeshNode * n = cast2Node( it->next() );
962     i++;
963     bool isDiag = ( n == theNode1 || n == theNode2 );
964     if ( !isDiag )
965       n4 = n;
966   }
967   // Make an array of nodes to be in a quadrangle
968   int iNode = 0, iFirstDiag = -1;
969   it = tr1->nodesIterator();
970   i=0;
971   while ( i<3 ) {
972     const SMDS_MeshNode * n = cast2Node( it->next() );
973     i++;
974     bool isDiag = ( n == theNode1 || n == theNode2 );
975     if ( isDiag ) {
976       if ( iFirstDiag < 0 )
977         iFirstDiag = iNode;
978       else if ( iNode - iFirstDiag == 1 )
979         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
980     }
981     else if ( n == n4 ) {
982       return false; // tr1 and tr2 should not have all the same nodes
983     }
984     theQuadNodes[ iNode++ ] = n;
985   }
986   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
987     theQuadNodes[ iNode ] = n4;
988
989   return true;
990 }
991
992 //=======================================================================
993 //function : DeleteDiag
994 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
995 //           with a quadrangle built on the same 4 nodes.
996 //           Return false if proper faces not found
997 //=======================================================================
998
999 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1000                                    const SMDS_MeshNode * theNode2)
1001 {
1002   ClearLastCreated();
1003
1004   const SMDS_MeshElement *tr1, *tr2;
1005   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1006     return false;
1007
1008   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1009        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1010     return false;
1011
1012   SMESHDS_Mesh * aMesh = GetMeshDS();
1013
1014   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1015       (tr2->GetEntityType() == SMDSEntity_Triangle))
1016   {
1017     const SMDS_MeshNode* aNodes [ 4 ];
1018     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1019       return false;
1020
1021     const SMDS_MeshElement* newElem = 0;
1022     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1023     myLastCreatedElems.push_back(newElem);
1024     AddToSameGroups( newElem, tr1, aMesh );
1025     int aShapeId = tr1->getshapeId();
1026     if ( aShapeId )
1027       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1028
1029     aMesh->RemoveElement( tr1 );
1030     aMesh->RemoveElement( tr2 );
1031
1032     return true;
1033   }
1034
1035   // check case of quadratic faces
1036   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1037     return false;
1038   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1039     return false;
1040
1041   //       5
1042   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1043   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1044   //    |   / |
1045   //  7 +  +  + 6
1046   //    | /9  |
1047   //    |/    |
1048   //  4 +--+--+ 3
1049   //       8
1050
1051   vector< const SMDS_MeshNode* > N1;
1052   vector< const SMDS_MeshNode* > N2;
1053   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1054     return false;
1055   // now we receive following N1 and N2 (using numeration as above image)
1056   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1057   // i.e. first nodes from both arrays determ new diagonal
1058
1059   const SMDS_MeshNode* aNodes[8];
1060   aNodes[0] = N1[0];
1061   aNodes[1] = N1[1];
1062   aNodes[2] = N2[0];
1063   aNodes[3] = N2[1];
1064   aNodes[4] = N1[3];
1065   aNodes[5] = N2[5];
1066   aNodes[6] = N2[3];
1067   aNodes[7] = N1[5];
1068
1069   const SMDS_MeshElement* newElem = 0;
1070   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1071                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1072   myLastCreatedElems.push_back(newElem);
1073   AddToSameGroups( newElem, tr1, aMesh );
1074   int aShapeId = tr1->getshapeId();
1075   if ( aShapeId )
1076   {
1077     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1078   }
1079   aMesh->RemoveElement( tr1 );
1080   aMesh->RemoveElement( tr2 );
1081
1082   // remove middle node (9)
1083   GetMeshDS()->RemoveNode( N1[4] );
1084
1085   return true;
1086 }
1087
1088 //=======================================================================
1089 //function : Reorient
1090 //purpose  : Reverse theElement orientation
1091 //=======================================================================
1092
1093 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1094 {
1095   ClearLastCreated();
1096
1097   if (!theElem)
1098     return false;
1099   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1100   if ( !it || !it->more() )
1101     return false;
1102
1103   const SMDSAbs_ElementType type = theElem->GetType();
1104   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1105     return false;
1106
1107   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1108   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1109   {
1110     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1111     if (!aPolyedre) {
1112       MESSAGE("Warning: bad volumic element");
1113       return false;
1114     }
1115     const int nbFaces = aPolyedre->NbFaces();
1116     vector<const SMDS_MeshNode *> poly_nodes;
1117     vector<int> quantities (nbFaces);
1118
1119     // reverse each face of the polyedre
1120     for (int iface = 1; iface <= nbFaces; iface++) {
1121       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1122       quantities[iface - 1] = nbFaceNodes;
1123
1124       for (inode = nbFaceNodes; inode >= 1; inode--) {
1125         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1126         poly_nodes.push_back(curNode);
1127       }
1128     }
1129     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1130   }
1131   else // other elements
1132   {
1133     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1134     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1135     if ( interlace.empty() )
1136     {
1137       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1138     }
1139     else
1140     {
1141       SMDS_MeshCell::applyInterlace( interlace, nodes );
1142     }
1143     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1144   }
1145   return false;
1146 }
1147
1148 //================================================================================
1149 /*!
1150  * \brief Reorient faces.
1151  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1152  * \param theDirection - desired direction of normal of \a theFace
1153  * \param theFace - one of \a theFaces that should be oriented according to
1154  *        \a theDirection and whose orientation defines orientation of other faces
1155  * \return number of reoriented faces.
1156  */
1157 //================================================================================
1158
1159 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1160                                   const gp_Dir&            theDirection,
1161                                   const SMDS_MeshElement * theFace)
1162 {
1163   int nbReori = 0;
1164   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1165
1166   if ( theFaces.empty() )
1167   {
1168     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1169     while ( fIt->more() )
1170       theFaces.insert( theFaces.end(), fIt->next() );
1171   }
1172
1173   // orient theFace according to theDirection
1174   gp_XYZ normal;
1175   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1176   if ( normal * theDirection.XYZ() < 0 )
1177     nbReori += Reorient( theFace );
1178
1179   // Orient other faces
1180
1181   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1182   TIDSortedElemSet avoidSet;
1183   set< SMESH_TLink > checkedLinks;
1184   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1185
1186   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1187     theFaces.erase( theFace );
1188   startFaces.insert( theFace );
1189
1190   int nodeInd1, nodeInd2;
1191   const SMDS_MeshElement*           otherFace;
1192   vector< const SMDS_MeshElement* > facesNearLink;
1193   vector< std::pair< int, int > >   nodeIndsOfFace;
1194
1195   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1196   while ( !startFaces.empty() )
1197   {
1198     startFace = startFaces.begin();
1199     theFace = *startFace;
1200     startFaces.erase( startFace );
1201     if ( !visitedFaces.insert( theFace ).second )
1202       continue;
1203
1204     avoidSet.clear();
1205     avoidSet.insert(theFace);
1206
1207     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1208
1209     const int nbNodes = theFace->NbCornerNodes();
1210     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1211     {
1212       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1213       linkIt_isNew = checkedLinks.insert( link );
1214       if ( !linkIt_isNew.second )
1215       {
1216         // link has already been checked and won't be encountered more
1217         // if the group (theFaces) is manifold
1218         //checkedLinks.erase( linkIt_isNew.first );
1219       }
1220       else
1221       {
1222         facesNearLink.clear();
1223         nodeIndsOfFace.clear();
1224         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1225                                                              theFaces, avoidSet,
1226                                                              &nodeInd1, &nodeInd2 )))
1227           if ( otherFace != theFace)
1228           {
1229             facesNearLink.push_back( otherFace );
1230             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1231             avoidSet.insert( otherFace );
1232           }
1233         if ( facesNearLink.size() > 1 )
1234         {
1235           // NON-MANIFOLD mesh shell !
1236           // select a face most co-directed with theFace,
1237           // other faces won't be visited this time
1238           gp_XYZ NF, NOF;
1239           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1240           double proj, maxProj = -1;
1241           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1242             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1243             if (( proj = Abs( NF * NOF )) > maxProj ) {
1244               maxProj = proj;
1245               otherFace = facesNearLink[i];
1246               nodeInd1  = nodeIndsOfFace[i].first;
1247               nodeInd2  = nodeIndsOfFace[i].second;
1248             }
1249           }
1250           // not to visit rejected faces
1251           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1252             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1253               visitedFaces.insert( facesNearLink[i] );
1254         }
1255         else if ( facesNearLink.size() == 1 )
1256         {
1257           otherFace = facesNearLink[0];
1258           nodeInd1  = nodeIndsOfFace.back().first;
1259           nodeInd2  = nodeIndsOfFace.back().second;
1260         }
1261         if ( otherFace && otherFace != theFace)
1262         {
1263           // link must be reverse in otherFace if orientation to otherFace
1264           // is same as that of theFace
1265           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1266           {
1267             nbReori += Reorient( otherFace );
1268           }
1269           startFaces.insert( otherFace );
1270         }
1271       }
1272       std::swap( link.first, link.second ); // reverse the link
1273     }
1274   }
1275   return nbReori;
1276 }
1277
1278 //================================================================================
1279 /*!
1280  * \brief Reorient faces basing on orientation of adjacent volumes.
1281  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1282  * \param theVolumes - reference volumes.
1283  * \param theOutsideNormal - to orient faces to have their normal
1284  *        pointing either \a outside or \a inside the adjacent volumes.
1285  * \return number of reoriented faces.
1286  */
1287 //================================================================================
1288
1289 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1290                                       TIDSortedElemSet & theVolumes,
1291                                       const bool         theOutsideNormal)
1292 {
1293   int nbReori = 0;
1294
1295   SMDS_ElemIteratorPtr faceIt;
1296   if ( theFaces.empty() )
1297     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1298   else
1299     faceIt = SMESHUtils::elemSetIterator( theFaces );
1300
1301   vector< const SMDS_MeshNode* > faceNodes;
1302   TIDSortedElemSet checkedVolumes;
1303   set< const SMDS_MeshNode* > faceNodesSet;
1304   SMDS_VolumeTool volumeTool;
1305
1306   while ( faceIt->more() ) // loop on given faces
1307   {
1308     const SMDS_MeshElement* face = faceIt->next();
1309     if ( face->GetType() != SMDSAbs_Face )
1310       continue;
1311
1312     const size_t nbCornersNodes = face->NbCornerNodes();
1313     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1314
1315     checkedVolumes.clear();
1316     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1317     while ( vIt->more() )
1318     {
1319       const SMDS_MeshElement* volume = vIt->next();
1320
1321       if ( !checkedVolumes.insert( volume ).second )
1322         continue;
1323       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1324         continue;
1325
1326       // is volume adjacent?
1327       bool allNodesCommon = true;
1328       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1329         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1330       if ( !allNodesCommon )
1331         continue;
1332
1333       // get nodes of a corresponding volume facet
1334       faceNodesSet.clear();
1335       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1336       volumeTool.Set( volume );
1337       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1338       if ( facetID < 0 ) continue;
1339       volumeTool.SetExternalNormal();
1340       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1341
1342       // compare order of faceNodes and facetNodes
1343       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1344       int iNN[2];
1345       for ( int i = 0; i < 2; ++i )
1346       {
1347         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1348         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1349           if ( faceNodes[ iN ] == n )
1350           {
1351             iNN[ i ] = iN;
1352             break;
1353           }
1354       }
1355       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1356       if ( isOutside != theOutsideNormal )
1357         nbReori += Reorient( face );
1358     }
1359   }  // loop on given faces
1360
1361   return nbReori;
1362 }
1363
1364 //=======================================================================
1365 //function : getBadRate
1366 //purpose  :
1367 //=======================================================================
1368
1369 static double getBadRate (const SMDS_MeshElement*               theElem,
1370                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1371 {
1372   SMESH::Controls::TSequenceOfXYZ P;
1373   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1374     return 1e100;
1375   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1376   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1377 }
1378
1379 //=======================================================================
1380 //function : QuadToTri
1381 //purpose  : Cut quadrangles into triangles.
1382 //           theCrit is used to select a diagonal to cut
1383 //=======================================================================
1384
1385 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1386                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1387 {
1388   ClearLastCreated();
1389
1390   if ( !theCrit.get() )
1391     return false;
1392
1393   SMESHDS_Mesh *       aMesh = GetMeshDS();
1394   Handle(Geom_Surface) surface;
1395   SMESH_MesherHelper   helper( *GetMesh() );
1396
1397   myLastCreatedElems.reserve( theElems.size() * 2 );
1398
1399   TIDSortedElemSet::iterator itElem;
1400   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1401   {
1402     const SMDS_MeshElement* elem = *itElem;
1403     if ( !elem || elem->GetType() != SMDSAbs_Face )
1404       continue;
1405     if ( elem->NbCornerNodes() != 4 )
1406       continue;
1407
1408     // retrieve element nodes
1409     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1410
1411     // compare two sets of possible triangles
1412     double aBadRate1, aBadRate2; // to what extent a set is bad
1413     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1414     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1415     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1416
1417     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1418     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1419     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1420
1421     const int aShapeId = FindShape( elem );
1422     const SMDS_MeshElement* newElem1 = 0;
1423     const SMDS_MeshElement* newElem2 = 0;
1424
1425     if ( !elem->IsQuadratic() ) // split linear quadrangle
1426     {
1427       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1428       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1429       if ( aBadRate1 <= aBadRate2 ) {
1430         // tr1 + tr2 is better
1431         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1432         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1433       }
1434       else {
1435         // tr3 + tr4 is better
1436         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1437         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1438       }
1439     }
1440     else // split quadratic quadrangle
1441     {
1442       helper.SetIsQuadratic( true );
1443       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1444
1445       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1446       if ( aNodes.size() == 9 )
1447       {
1448         helper.SetIsBiQuadratic( true );
1449         if ( aBadRate1 <= aBadRate2 )
1450           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1451         else
1452           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1453       }
1454       // create a new element
1455       if ( aBadRate1 <= aBadRate2 ) {
1456         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1457         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1458       }
1459       else {
1460         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1461         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1462       }
1463     } // quadratic case
1464
1465     // care of a new element
1466
1467     myLastCreatedElems.push_back(newElem1);
1468     myLastCreatedElems.push_back(newElem2);
1469     AddToSameGroups( newElem1, elem, aMesh );
1470     AddToSameGroups( newElem2, elem, aMesh );
1471
1472     // put a new triangle on the same shape
1473     if ( aShapeId )
1474       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1475     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1476
1477     aMesh->RemoveElement( elem );
1478   }
1479   return true;
1480 }
1481
1482 //=======================================================================
1483 /*!
1484  * \brief Split each of given quadrangles into 4 triangles.
1485  * \param theElems - The faces to be split. If empty all faces are split.
1486  */
1487 //=======================================================================
1488
1489 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1490 {
1491   ClearLastCreated();
1492   myLastCreatedElems.reserve( theElems.size() * 4 );
1493
1494   SMESH_MesherHelper helper( *GetMesh() );
1495   helper.SetElementsOnShape( true );
1496
1497   SMDS_ElemIteratorPtr faceIt;
1498   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1499   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1500
1501   bool   checkUV;
1502   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1503   gp_XYZ xyz[9];
1504   vector< const SMDS_MeshNode* > nodes;
1505   SMESHDS_SubMesh*               subMeshDS = 0;
1506   TopoDS_Face                    F;
1507   Handle(Geom_Surface)           surface;
1508   TopLoc_Location                loc;
1509
1510   while ( faceIt->more() )
1511   {
1512     const SMDS_MeshElement* quad = faceIt->next();
1513     if ( !quad || quad->NbCornerNodes() != 4 )
1514       continue;
1515
1516     // get a surface the quad is on
1517
1518     if ( quad->getshapeId() < 1 )
1519     {
1520       F.Nullify();
1521       helper.SetSubShape( 0 );
1522       subMeshDS = 0;
1523     }
1524     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1525     {
1526       helper.SetSubShape( quad->getshapeId() );
1527       if ( !helper.GetSubShape().IsNull() &&
1528            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1529       {
1530         F = TopoDS::Face( helper.GetSubShape() );
1531         surface = BRep_Tool::Surface( F, loc );
1532         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1533       }
1534       else
1535       {
1536         helper.SetSubShape( 0 );
1537         subMeshDS = 0;
1538       }
1539     }
1540
1541     // create a central node
1542
1543     const SMDS_MeshNode* nCentral;
1544     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1545
1546     if ( nodes.size() == 9 )
1547     {
1548       nCentral = nodes.back();
1549     }
1550     else
1551     {
1552       size_t iN = 0;
1553       if ( F.IsNull() )
1554       {
1555         for ( ; iN < nodes.size(); ++iN )
1556           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1557
1558         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1559           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1560
1561         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1562                                    xyz[0], xyz[1], xyz[2], xyz[3],
1563                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1564       }
1565       else
1566       {
1567         for ( ; iN < nodes.size(); ++iN )
1568           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1569
1570         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1571           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1572
1573         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1574                                   uv[0], uv[1], uv[2], uv[3],
1575                                   uv[4], uv[5], uv[6], uv[7] );
1576
1577         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1578         xyz[ 8 ] = p.XYZ();
1579       }
1580
1581       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1582                                  uv[8].X(), uv[8].Y() );
1583       myLastCreatedNodes.push_back( nCentral );
1584     }
1585
1586     // create 4 triangles
1587
1588     helper.SetIsQuadratic  ( nodes.size() > 4 );
1589     helper.SetIsBiQuadratic( nodes.size() == 9 );
1590     if ( helper.GetIsQuadratic() )
1591       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1592
1593     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1594
1595     for ( int i = 0; i < 4; ++i )
1596     {
1597       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1598                                                nodes[(i+1)%4],
1599                                                nCentral );
1600       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1601       myLastCreatedElems.push_back( tria );
1602     }
1603   }
1604 }
1605
1606 //=======================================================================
1607 //function : BestSplit
1608 //purpose  : Find better diagonal for cutting.
1609 //=======================================================================
1610
1611 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1612                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1613 {
1614   ClearLastCreated();
1615
1616   if (!theCrit.get())
1617     return -1;
1618
1619   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1620     return -1;
1621
1622   if( theQuad->NbNodes()==4 ||
1623       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1624
1625     // retrieve element nodes
1626     const SMDS_MeshNode* aNodes [4];
1627     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1628     int i = 0;
1629     //while (itN->more())
1630     while (i<4) {
1631       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1632     }
1633     // compare two sets of possible triangles
1634     double aBadRate1, aBadRate2; // to what extent a set is bad
1635     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1636     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1637     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1638
1639     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1640     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1641     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1642     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1643     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1644     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1645       return 1; // diagonal 1-3
1646
1647     return 2; // diagonal 2-4
1648   }
1649   return -1;
1650 }
1651
1652 namespace
1653 {
1654   // Methods of splitting volumes into tetra
1655
1656   const int theHexTo5_1[5*4+1] =
1657     {
1658       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1659     };
1660   const int theHexTo5_2[5*4+1] =
1661     {
1662       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1663     };
1664   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1665
1666   const int theHexTo6_1[6*4+1] =
1667     {
1668       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
1669     };
1670   const int theHexTo6_2[6*4+1] =
1671     {
1672       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
1673     };
1674   const int theHexTo6_3[6*4+1] =
1675     {
1676       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
1677     };
1678   const int theHexTo6_4[6*4+1] =
1679     {
1680       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
1681     };
1682   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1683
1684   const int thePyraTo2_1[2*4+1] =
1685     {
1686       0, 1, 2, 4,    0, 2, 3, 4,   -1
1687     };
1688   const int thePyraTo2_2[2*4+1] =
1689     {
1690       1, 2, 3, 4,    1, 3, 0, 4,   -1
1691     };
1692   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1693
1694   const int thePentaTo3_1[3*4+1] =
1695     {
1696       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1697     };
1698   const int thePentaTo3_2[3*4+1] =
1699     {
1700       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1701     };
1702   const int thePentaTo3_3[3*4+1] =
1703     {
1704       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1705     };
1706   const int thePentaTo3_4[3*4+1] =
1707     {
1708       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1709     };
1710   const int thePentaTo3_5[3*4+1] =
1711     {
1712       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1713     };
1714   const int thePentaTo3_6[3*4+1] =
1715     {
1716       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1717     };
1718   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1719                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1720
1721   // Methods of splitting hexahedron into prisms
1722
1723   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1724     {
1725       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
1726     };
1727   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1728     {
1729       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
1730     };
1731   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1732     {
1733       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
1734     };
1735
1736   const int theHexTo2Prisms_BT_1[6*2+1] =
1737     {
1738       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1739     };
1740   const int theHexTo2Prisms_BT_2[6*2+1] =
1741     {
1742       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1743     };
1744   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1745
1746   const int theHexTo2Prisms_LR_1[6*2+1] =
1747     {
1748       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1749     };
1750   const int theHexTo2Prisms_LR_2[6*2+1] =
1751     {
1752       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1753     };
1754   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1755
1756   const int theHexTo2Prisms_FB_1[6*2+1] =
1757     {
1758       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1759     };
1760   const int theHexTo2Prisms_FB_2[6*2+1] =
1761     {
1762       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1763     };
1764   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1765
1766
1767   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1768   {
1769     int _n1, _n2, _n3;
1770     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1771     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1772     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1773                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1774   };
1775   struct TSplitMethod
1776   {
1777     int        _nbSplits;
1778     int        _nbCorners;
1779     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1780     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1781     bool       _ownConn;      //!< to delete _connectivity in destructor
1782     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1783
1784     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1785       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1786     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1787     bool hasFacet( const TTriangleFacet& facet ) const
1788     {
1789       if ( _nbCorners == 4 )
1790       {
1791         const int* tetConn = _connectivity;
1792         for ( ; tetConn[0] >= 0; tetConn += 4 )
1793           if (( facet.contains( tetConn[0] ) +
1794                 facet.contains( tetConn[1] ) +
1795                 facet.contains( tetConn[2] ) +
1796                 facet.contains( tetConn[3] )) == 3 )
1797             return true;
1798       }
1799       else // prism, _nbCorners == 6
1800       {
1801         const int* prismConn = _connectivity;
1802         for ( ; prismConn[0] >= 0; prismConn += 6 )
1803         {
1804           if (( facet.contains( prismConn[0] ) &&
1805                 facet.contains( prismConn[1] ) &&
1806                 facet.contains( prismConn[2] ))
1807               ||
1808               ( facet.contains( prismConn[3] ) &&
1809                 facet.contains( prismConn[4] ) &&
1810                 facet.contains( prismConn[5] )))
1811             return true;
1812         }
1813       }
1814       return false;
1815     }
1816   };
1817
1818   //=======================================================================
1819   /*!
1820    * \brief return TSplitMethod for the given element to split into tetrahedra
1821    */
1822   //=======================================================================
1823
1824   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1825   {
1826     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1827
1828     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1829     // an edge and a face barycenter; tertaherdons are based on triangles and
1830     // a volume barycenter
1831     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1832
1833     // Find out how adjacent volumes are split
1834
1835     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1836     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1837     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1838     {
1839       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1840       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1841       if ( nbNodes < 4 ) continue;
1842
1843       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1844       const int* nInd = vol.GetFaceNodesIndices( iF );
1845       if ( nbNodes == 4 )
1846       {
1847         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1848         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1849         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1850         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1851       }
1852       else
1853       {
1854         int iCom = 0; // common node of triangle faces to split into
1855         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1856         {
1857           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1858                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1859                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1860           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1861                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1862                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1863           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1864           {
1865             triaSplits.push_back( t012 );
1866             triaSplits.push_back( t023 );
1867             break;
1868           }
1869         }
1870       }
1871       if ( !triaSplits.empty() )
1872         hasAdjacentSplits = true;
1873     }
1874
1875     // Among variants of split method select one compliant with adjacent volumes
1876
1877     TSplitMethod method;
1878     if ( !vol.Element()->IsPoly() && !is24TetMode )
1879     {
1880       int nbVariants = 2, nbTet = 0;
1881       const int** connVariants = 0;
1882       switch ( vol.Element()->GetEntityType() )
1883       {
1884       case SMDSEntity_Hexa:
1885       case SMDSEntity_Quad_Hexa:
1886       case SMDSEntity_TriQuad_Hexa:
1887         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1888           connVariants = theHexTo5, nbTet = 5;
1889         else
1890           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1891         break;
1892       case SMDSEntity_Pyramid:
1893       case SMDSEntity_Quad_Pyramid:
1894         connVariants = thePyraTo2;  nbTet = 2;
1895         break;
1896       case SMDSEntity_Penta:
1897       case SMDSEntity_Quad_Penta:
1898       case SMDSEntity_BiQuad_Penta:
1899         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1900         break;
1901       default:
1902         nbVariants = 0;
1903       }
1904       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1905       {
1906         // check method compliancy with adjacent tetras,
1907         // all found splits must be among facets of tetras described by this method
1908         method = TSplitMethod( nbTet, connVariants[variant] );
1909         if ( hasAdjacentSplits && method._nbSplits > 0 )
1910         {
1911           bool facetCreated = true;
1912           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1913           {
1914             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1915             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1916               facetCreated = method.hasFacet( *facet );
1917           }
1918           if ( !facetCreated )
1919             method = TSplitMethod(0); // incompatible method
1920         }
1921       }
1922     }
1923     if ( method._nbSplits < 1 )
1924     {
1925       // No standard method is applicable, use a generic solution:
1926       // each facet of a volume is split into triangles and
1927       // each of triangles and a volume barycenter form a tetrahedron.
1928
1929       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1930
1931       int* connectivity = new int[ maxTetConnSize + 1 ];
1932       method._connectivity = connectivity;
1933       method._ownConn = true;
1934       method._baryNode = !isHex27; // to create central node or not
1935
1936       int connSize = 0;
1937       int baryCenInd = vol.NbNodes() - int( isHex27 );
1938       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1939       {
1940         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1941         const int*   nInd = vol.GetFaceNodesIndices( iF );
1942         // find common node of triangle facets of tetra to create
1943         int iCommon = 0; // index in linear numeration
1944         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1945         if ( !triaSplits.empty() )
1946         {
1947           // by found facets
1948           const TTriangleFacet* facet = &triaSplits.front();
1949           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1950             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1951                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1952               break;
1953         }
1954         else if ( nbNodes > 3 && !is24TetMode )
1955         {
1956           // find the best method of splitting into triangles by aspect ratio
1957           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1958           map< double, int > badness2iCommon;
1959           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1960           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1961           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1962           {
1963             double badness = 0;
1964             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1965             {
1966               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1967                                       nodes[ iQ*((iLast-1)%nbNodes)],
1968                                       nodes[ iQ*((iLast  )%nbNodes)]);
1969               badness += getBadRate( &tria, aspectRatio );
1970             }
1971             badness2iCommon.insert( make_pair( badness, iCommon ));
1972           }
1973           // use iCommon with lowest badness
1974           iCommon = badness2iCommon.begin()->second;
1975         }
1976         if ( iCommon >= nbNodes )
1977           iCommon = 0; // something wrong
1978
1979         // fill connectivity of tetrahedra based on a current face
1980         int nbTet = nbNodes - 2;
1981         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1982         {
1983           int faceBaryCenInd;
1984           if ( isHex27 )
1985           {
1986             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1987             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1988           }
1989           else
1990           {
1991             method._faceBaryNode[ iF ] = 0;
1992             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1993           }
1994           nbTet = nbNodes;
1995           for ( int i = 0; i < nbTet; ++i )
1996           {
1997             int i1 = i, i2 = (i+1) % nbNodes;
1998             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1999             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2000             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2001             connectivity[ connSize++ ] = faceBaryCenInd;
2002             connectivity[ connSize++ ] = baryCenInd;
2003           }
2004         }
2005         else
2006         {
2007           for ( int i = 0; i < nbTet; ++i )
2008           {
2009             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2010             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2011             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2012             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2013             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2014             connectivity[ connSize++ ] = baryCenInd;
2015           }
2016         }
2017         method._nbSplits += nbTet;
2018
2019       } // loop on volume faces
2020
2021       connectivity[ connSize++ ] = -1;
2022
2023     } // end of generic solution
2024
2025     return method;
2026   }
2027   //=======================================================================
2028   /*!
2029    * \brief return TSplitMethod to split haxhedron into prisms
2030    */
2031   //=======================================================================
2032
2033   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2034                                     const int        methodFlags,
2035                                     const int        facetToSplit)
2036   {
2037     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2038     // B, T, L, B, R, F
2039     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2040
2041     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2042     {
2043       static TSplitMethod to4methods[4]; // order BT, LR, FB
2044       if ( to4methods[iF]._nbSplits == 0 )
2045       {
2046         switch ( iF ) {
2047         case 0:
2048           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2049           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2050           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2051           break;
2052         case 1:
2053           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2054           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2055           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2056           break;
2057         case 2:
2058           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2059           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2060           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2061           break;
2062         default: return to4methods[3];
2063         }
2064         to4methods[iF]._nbSplits  = 4;
2065         to4methods[iF]._nbCorners = 6;
2066       }
2067       return to4methods[iF];
2068     }
2069     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2070
2071     TSplitMethod method;
2072
2073     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074
2075     const int nbVariants = 2, nbSplits = 2;
2076     const int** connVariants = 0;
2077     switch ( iF ) {
2078     case 0: connVariants = theHexTo2Prisms_BT; break;
2079     case 1: connVariants = theHexTo2Prisms_LR; break;
2080     case 2: connVariants = theHexTo2Prisms_FB; break;
2081     default: return method;
2082     }
2083
2084     // look for prisms adjacent via facetToSplit and an opposite one
2085     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2086     {
2087       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2088       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2089       if ( nbNodes != 4 ) return method;
2090
2091       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2092       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2093       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2094       TTriangleFacet* t;
2095       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2096         t = &t012;
2097       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2098         t = &t123;
2099       else
2100         continue;
2101
2102       // there are adjacent prism
2103       for ( int variant = 0; variant < nbVariants; ++variant )
2104       {
2105         // check method compliancy with adjacent prisms,
2106         // the found prism facets must be among facets of prisms described by current method
2107         method._nbSplits     = nbSplits;
2108         method._nbCorners    = 6;
2109         method._connectivity = connVariants[ variant ];
2110         if ( method.hasFacet( *t ))
2111           return method;
2112       }
2113     }
2114
2115     // No adjacent prisms. Select a variant with a best aspect ratio.
2116
2117     double badness[2] = { 0., 0. };
2118     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2119     const SMDS_MeshNode** nodes = vol.GetNodes();
2120     for ( int variant = 0; variant < nbVariants; ++variant )
2121       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2122       {
2123         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2124         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2125
2126         method._connectivity = connVariants[ variant ];
2127         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2129         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2130
2131         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2132                                 nodes[ t->_n2 ],
2133                                 nodes[ t->_n3 ] );
2134         badness[ variant ] += getBadRate( &tria, aspectRatio );
2135       }
2136     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2137
2138     method._nbSplits     = nbSplits;
2139     method._nbCorners    = 6;
2140     method._connectivity = connVariants[ iBetter ];
2141
2142     return method;
2143   }
2144
2145   //================================================================================
2146   /*!
2147    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2148    */
2149   //================================================================================
2150
2151   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2152                                        const SMDSAbs_GeometryType geom ) const
2153   {
2154     // find the tetrahedron including the three nodes of facet
2155     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2156     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2157     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2158     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2159     while ( volIt1->more() )
2160     {
2161       const SMDS_MeshElement* v = volIt1->next();
2162       if ( v->GetGeomType() != geom )
2163         continue;
2164       const int lastCornerInd = v->NbCornerNodes() - 1;
2165       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2166         continue; // medium node not allowed
2167       const int ind2 = v->GetNodeIndex( n2 );
2168       if ( ind2 < 0 || lastCornerInd < ind2 )
2169         continue;
2170       const int ind3 = v->GetNodeIndex( n3 );
2171       if ( ind3 < 0 || lastCornerInd < ind3 )
2172         continue;
2173       return true;
2174     }
2175     return false;
2176   }
2177
2178   //=======================================================================
2179   /*!
2180    * \brief A key of a face of volume
2181    */
2182   //=======================================================================
2183
2184   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2185   {
2186     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2187     {
2188       TIDSortedNodeSet sortedNodes;
2189       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2190       int nbNodes = vol.NbFaceNodes( iF );
2191       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2192       for ( int i = 0; i < nbNodes; i += iQ )
2193         sortedNodes.insert( fNodes[i] );
2194       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2195       first.first   = (*(n++))->GetID();
2196       first.second  = (*(n++))->GetID();
2197       second.first  = (*(n++))->GetID();
2198       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2199     }
2200   };
2201 } // namespace
2202
2203 //=======================================================================
2204 //function : SplitVolumes
2205 //purpose  : Split volume elements into tetrahedra or prisms.
2206 //           If facet ID < 0, element is split into tetrahedra,
2207 //           else a hexahedron is split into prisms so that the given facet is
2208 //           split into triangles
2209 //=======================================================================
2210
2211 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2212                                      const int            theMethodFlags)
2213 {
2214   SMDS_VolumeTool    volTool;
2215   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2216   fHelper.ToFixNodeParameters( true );
2217
2218   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2219   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2220
2221   SMESH_SequenceOfElemPtr newNodes, newElems;
2222
2223   // map face of volume to it's baricenrtic node
2224   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2225   double bc[3];
2226   vector<const SMDS_MeshElement* > splitVols;
2227
2228   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2229   for ( ; elem2facet != theElems.end(); ++elem2facet )
2230   {
2231     const SMDS_MeshElement* elem = elem2facet->first;
2232     const int       facetToSplit = elem2facet->second;
2233     if ( elem->GetType() != SMDSAbs_Volume )
2234       continue;
2235     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2236     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2237       continue;
2238
2239     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2240
2241     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2242                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2243                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2244     if ( splitMethod._nbSplits < 1 ) continue;
2245
2246     // find submesh to add new tetras to
2247     if ( !subMesh || !subMesh->Contains( elem ))
2248     {
2249       int shapeID = FindShape( elem );
2250       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2251       subMesh = GetMeshDS()->MeshElements( shapeID );
2252     }
2253     int iQ;
2254     if ( elem->IsQuadratic() )
2255     {
2256       iQ = 2;
2257       // add quadratic links to the helper
2258       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2259       {
2260         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2261         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2262         for ( int iN = 0; iN < nbN; iN += iQ )
2263           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2264       }
2265       helper.SetIsQuadratic( true );
2266     }
2267     else
2268     {
2269       iQ = 1;
2270       helper.SetIsQuadratic( false );
2271     }
2272     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2273                                         volTool.GetNodes() + elem->NbNodes() );
2274     helper.SetElementsOnShape( true );
2275     if ( splitMethod._baryNode )
2276     {
2277       // make a node at barycenter
2278       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2279       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2280       nodes.push_back( gcNode );
2281       newNodes.push_back( gcNode );
2282     }
2283     if ( !splitMethod._faceBaryNode.empty() )
2284     {
2285       // make or find baricentric nodes of faces
2286       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2287       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2288       {
2289         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2290           volFace2BaryNode.insert
2291           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2292         if ( !f_n->second )
2293         {
2294           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2295           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2296         }
2297         nodes.push_back( iF_n->second = f_n->second );
2298       }
2299     }
2300
2301     // make new volumes
2302     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2303     const int* volConn = splitMethod._connectivity;
2304     if ( splitMethod._nbCorners == 4 ) // tetra
2305       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2306         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2307                                                                nodes[ volConn[1] ],
2308                                                                nodes[ volConn[2] ],
2309                                                                nodes[ volConn[3] ]));
2310     else // prisms
2311       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2312         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2313                                                                nodes[ volConn[1] ],
2314                                                                nodes[ volConn[2] ],
2315                                                                nodes[ volConn[3] ],
2316                                                                nodes[ volConn[4] ],
2317                                                                nodes[ volConn[5] ]));
2318
2319     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2320
2321     // Split faces on sides of the split volume
2322
2323     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2324     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2325     {
2326       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2327       if ( nbNodes < 4 ) continue;
2328
2329       // find an existing face
2330       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2331                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2332       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2333                                                                        /*noMedium=*/false))
2334       {
2335         // make triangles
2336         helper.SetElementsOnShape( false );
2337         vector< const SMDS_MeshElement* > triangles;
2338
2339         // find submesh to add new triangles in
2340         if ( !fSubMesh || !fSubMesh->Contains( face ))
2341         {
2342           int shapeID = FindShape( face );
2343           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2344         }
2345         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2346         if ( iF_n != splitMethod._faceBaryNode.end() )
2347         {
2348           const SMDS_MeshNode *baryNode = iF_n->second;
2349           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2350           {
2351             const SMDS_MeshNode* n1 = fNodes[iN];
2352             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2353             const SMDS_MeshNode *n3 = baryNode;
2354             if ( !volTool.IsFaceExternal( iF ))
2355               swap( n2, n3 );
2356             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2357           }
2358           if ( fSubMesh ) // update position of the bary node on geometry
2359           {
2360             if ( subMesh )
2361               subMesh->RemoveNode( baryNode );
2362             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2363             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2364             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2365             {
2366               fHelper.SetSubShape( s );
2367               gp_XY uv( 1e100, 1e100 );
2368               double distXYZ[4];
2369               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2370                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2371                    uv.X() < 1e100 )
2372               {
2373                 // node is too far from the surface
2374                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2375                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2376                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2377               }
2378             }
2379           }
2380         }
2381         else
2382         {
2383           // among possible triangles create ones described by split method
2384           const int* nInd = volTool.GetFaceNodesIndices( iF );
2385           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2386           int iCom = 0; // common node of triangle faces to split into
2387           list< TTriangleFacet > facets;
2388           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2389           {
2390             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2391                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2392                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2393             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2394                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2395                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2396             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2397             {
2398               facets.push_back( t012 );
2399               facets.push_back( t023 );
2400               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2401                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2402                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2403                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2404               break;
2405             }
2406           }
2407           list< TTriangleFacet >::iterator facet = facets.begin();
2408           if ( facet == facets.end() )
2409             break;
2410           for ( ; facet != facets.end(); ++facet )
2411           {
2412             if ( !volTool.IsFaceExternal( iF ))
2413               swap( facet->_n2, facet->_n3 );
2414             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2415                                                  volNodes[ facet->_n2 ],
2416                                                  volNodes[ facet->_n3 ]));
2417           }
2418         }
2419         for ( size_t i = 0; i < triangles.size(); ++i )
2420         {
2421           if ( !triangles[ i ]) continue;
2422           if ( fSubMesh )
2423             fSubMesh->AddElement( triangles[ i ]);
2424           newElems.push_back( triangles[ i ]);
2425         }
2426         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2427         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2428
2429       } // while a face based on facet nodes exists
2430     } // loop on volume faces to split them into triangles
2431
2432     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2433
2434     if ( geomType == SMDSEntity_TriQuad_Hexa )
2435     {
2436       // remove medium nodes that could become free
2437       for ( int i = 20; i < volTool.NbNodes(); ++i )
2438         if ( volNodes[i]->NbInverseElements() == 0 )
2439           GetMeshDS()->RemoveNode( volNodes[i] );
2440     }
2441   } // loop on volumes to split
2442
2443   myLastCreatedNodes = newNodes;
2444   myLastCreatedElems = newElems;
2445 }
2446
2447 //=======================================================================
2448 //function : GetHexaFacetsToSplit
2449 //purpose  : For hexahedra that will be split into prisms, finds facets to
2450 //           split into triangles. Only hexahedra adjacent to the one closest
2451 //           to theFacetNormal.Location() are returned.
2452 //param [in,out] theHexas - the hexahedra
2453 //param [in]     theFacetNormal - facet normal
2454 //param [out]    theFacets - the hexahedra and found facet IDs
2455 //=======================================================================
2456
2457 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2458                                              const gp_Ax1&     theFacetNormal,
2459                                              TFacetOfElem &    theFacets)
2460 {
2461 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2462
2463   // Find a hexa closest to the location of theFacetNormal
2464
2465   const SMDS_MeshElement* startHex;
2466   {
2467     // get SMDS_ElemIteratorPtr on theHexas
2468     typedef const SMDS_MeshElement*                                      TValue;
2469     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2470     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2471     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2472     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2473     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2474       ( new TElemSetIter( theHexas.begin(),
2475                           theHexas.end(),
2476                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2477
2478     SMESH_ElementSearcher* searcher =
2479       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2480
2481     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2482
2483     delete searcher;
2484
2485     if ( !startHex )
2486       throw SALOME_Exception( THIS_METHOD "startHex not found");
2487   }
2488
2489   // Select a facet of startHex by theFacetNormal
2490
2491   SMDS_VolumeTool vTool( startHex );
2492   double norm[3], dot, maxDot = 0;
2493   int facetID = -1;
2494   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2495     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2496     {
2497       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2498       if ( dot > maxDot )
2499       {
2500         facetID = iF;
2501         maxDot = dot;
2502       }
2503     }
2504   if ( facetID < 0 )
2505     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2506
2507   // Fill theFacets starting from facetID of startHex
2508
2509   // facets used for searching of volumes adjacent to already treated ones
2510   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2511   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2512   TFacetMap facetsToCheck;
2513
2514   set<const SMDS_MeshNode*> facetNodes;
2515   const SMDS_MeshElement*   curHex;
2516
2517   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2518
2519   while ( startHex )
2520   {
2521     // move in two directions from startHex via facetID
2522     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2523     {
2524       curHex       = startHex;
2525       int curFacet = facetID;
2526       if ( is2nd ) // do not treat startHex twice
2527       {
2528         vTool.Set( curHex );
2529         if ( vTool.IsFreeFace( curFacet, &curHex ))
2530         {
2531           curHex = 0;
2532         }
2533         else
2534         {
2535           vTool.GetFaceNodes( curFacet, facetNodes );
2536           vTool.Set( curHex );
2537           curFacet = vTool.GetFaceIndex( facetNodes );
2538         }
2539       }
2540       while ( curHex )
2541       {
2542         // store a facet to split
2543         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2544         {
2545           theFacets.insert( make_pair( curHex, -1 ));
2546           break;
2547         }
2548         if ( !allHex && !theHexas.count( curHex ))
2549           break;
2550
2551         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2552           theFacets.insert( make_pair( curHex, curFacet ));
2553         if ( !facetIt2isNew.second )
2554           break;
2555
2556         // remember not-to-split facets in facetsToCheck
2557         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2558         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2559         {
2560           if ( iF == curFacet && iF == oppFacet )
2561             continue;
2562           TVolumeFaceKey facetKey ( vTool, iF );
2563           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2564           pair< TFacetMap::iterator, bool > it2isnew =
2565             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2566           if ( !it2isnew.second )
2567             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2568         }
2569         // pass to a volume adjacent via oppFacet
2570         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2571         {
2572           curHex = 0;
2573         }
2574         else
2575         {
2576           // get a new curFacet
2577           vTool.GetFaceNodes( oppFacet, facetNodes );
2578           vTool.Set( curHex );
2579           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2580         }
2581       }
2582     } // move in two directions from startHex via facetID
2583
2584     // Find a new startHex by facetsToCheck
2585
2586     startHex = 0;
2587     facetID  = -1;
2588     TFacetMap::iterator fIt = facetsToCheck.begin();
2589     while ( !startHex && fIt != facetsToCheck.end() )
2590     {
2591       const TElemFacets&  elemFacets = fIt->second;
2592       const SMDS_MeshElement*    hex = elemFacets.first->first;
2593       int                 splitFacet = elemFacets.first->second;
2594       int               lateralFacet = elemFacets.second;
2595       facetsToCheck.erase( fIt );
2596       fIt = facetsToCheck.begin();
2597
2598       vTool.Set( hex );
2599       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2600            curHex->GetGeomType() != SMDSGeom_HEXA )
2601         continue;
2602       if ( !allHex && !theHexas.count( curHex ))
2603         continue;
2604
2605       startHex = curHex;
2606
2607       // find a facet of startHex to split
2608
2609       set<const SMDS_MeshNode*> lateralNodes;
2610       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2611       vTool.GetFaceNodes( splitFacet,   facetNodes );
2612       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2613       vTool.Set( startHex );
2614       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2615
2616       // look for a facet of startHex having common nodes with facetNodes
2617       // but not lateralFacet
2618       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2619       {
2620         if ( iF == lateralFacet )
2621           continue;
2622         int nbCommonNodes = 0;
2623         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2624         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2625           nbCommonNodes += facetNodes.count( nn[ iN ]);
2626
2627         if ( nbCommonNodes >= 2 )
2628         {
2629           facetID = iF;
2630           break;
2631         }
2632       }
2633       if ( facetID < 0 )
2634         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2635     }
2636   } //   while ( startHex )
2637
2638   return;
2639 }
2640
2641 namespace
2642 {
2643   //================================================================================
2644   /*!
2645    * \brief Selects nodes of several elements according to a given interlace
2646    *  \param [in] srcNodes - nodes to select from
2647    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2648    *  \param [in] interlace - indices of nodes for all elements
2649    *  \param [in] nbElems - nb of elements
2650    *  \param [in] nbNodes - nb of nodes in each element
2651    *  \param [in] mesh - the mesh
2652    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2653    *  \param [in] type - type of elements to look for
2654    */
2655   //================================================================================
2656
2657   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2658                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2659                     const int*                            interlace,
2660                     const int                             nbElems,
2661                     const int                             nbNodes,
2662                     SMESHDS_Mesh*                         mesh = 0,
2663                     list< const SMDS_MeshElement* >*      elemQueue=0,
2664                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2665   {
2666     for ( int iE = 0; iE < nbElems; ++iE )
2667     {
2668       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2669       const int*                         select = & interlace[iE*nbNodes];
2670       elemNodes.resize( nbNodes );
2671       for ( int iN = 0; iN < nbNodes; ++iN )
2672         elemNodes[iN] = srcNodes[ select[ iN ]];
2673     }
2674     const SMDS_MeshElement* e;
2675     if ( elemQueue )
2676       for ( int iE = 0; iE < nbElems; ++iE )
2677         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2678           elemQueue->push_back( e );
2679   }
2680 }
2681
2682 //=======================================================================
2683 /*
2684  * Split bi-quadratic elements into linear ones without creation of additional nodes
2685  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2686  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2687  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2688  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2689  *   will be split in order to keep the mesh conformal.
2690  *  \param elems - elements to split
2691  */
2692 //=======================================================================
2693
2694 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2695 {
2696   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2697   vector<const SMDS_MeshElement* > splitElems;
2698   list< const SMDS_MeshElement* > elemQueue;
2699   list< const SMDS_MeshElement* >::iterator elemIt;
2700
2701   SMESHDS_Mesh * mesh = GetMeshDS();
2702   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2703   int nbElems, nbNodes;
2704
2705   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2706   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2707   {
2708     elemQueue.clear();
2709     elemQueue.push_back( *elemSetIt );
2710     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2711     {
2712       const SMDS_MeshElement* elem = *elemIt;
2713       switch( elem->GetEntityType() )
2714       {
2715       case SMDSEntity_TriQuad_Hexa: // HEX27
2716       {
2717         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2718         nbElems  = nbNodes = 8;
2719         elemType = & hexaType;
2720
2721         // get nodes for new elements
2722         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2723                                  { 1,9,20,8,    17,22,26,21 },
2724                                  { 2,10,20,9,   18,23,26,22 },
2725                                  { 3,11,20,10,  19,24,26,23 },
2726                                  { 16,21,26,24, 4,12,25,15  },
2727                                  { 17,22,26,21, 5,13,25,12  },
2728                                  { 18,23,26,22, 6,14,25,13  },
2729                                  { 19,24,26,23, 7,15,25,14  }};
2730         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2731
2732         // add boundary faces to elemQueue
2733         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2734                                  { 4,5,6,7, 12,13,14,15, 25 },
2735                                  { 0,1,5,4, 8,17,12,16,  21 },
2736                                  { 1,2,6,5, 9,18,13,17,  22 },
2737                                  { 2,3,7,6, 10,19,14,18, 23 },
2738                                  { 3,0,4,7, 11,16,15,19, 24 }};
2739         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2740
2741         // add boundary segments to elemQueue
2742         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2743                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2744                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2745         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2746         break;
2747       }
2748       case SMDSEntity_BiQuad_Triangle: // TRIA7
2749       {
2750         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2751         nbElems = 3;
2752         nbNodes = 4;
2753         elemType = & quadType;
2754
2755         // get nodes for new elements
2756         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2757         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2758
2759         // add boundary segments to elemQueue
2760         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 4;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_Quad_Edge:
2781       {
2782         if ( elemIt == elemQueue.begin() )
2783           continue; // an elem is in theElems
2784         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785         nbElems = 2;
2786         nbNodes = 2;
2787         elemType = & segType;
2788
2789         // get nodes for new elements
2790         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2791         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2792         break;
2793       }
2794       default: continue;
2795       } // switch( elem->GetEntityType() )
2796
2797       // Create new elements
2798
2799       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2800
2801       splitElems.clear();
2802
2803       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2804       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2805       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2806       //elemType->SetID( -1 );
2807
2808       for ( int iE = 0; iE < nbElems; ++iE )
2809         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2810
2811
2812       ReplaceElemInGroups( elem, splitElems, mesh );
2813
2814       if ( subMesh )
2815         for ( size_t i = 0; i < splitElems.size(); ++i )
2816           subMesh->AddElement( splitElems[i] );
2817     }
2818   }
2819 }
2820
2821 //=======================================================================
2822 //function : AddToSameGroups
2823 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2824 //=======================================================================
2825
2826 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2827                                         const SMDS_MeshElement* elemInGroups,
2828                                         SMESHDS_Mesh *          aMesh)
2829 {
2830   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831   if (!groups.empty()) {
2832     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2833     for ( ; grIt != groups.end(); grIt++ ) {
2834       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2835       if ( group && group->Contains( elemInGroups ))
2836         group->SMDSGroup().Add( elemToAdd );
2837     }
2838   }
2839 }
2840
2841
2842 //=======================================================================
2843 //function : RemoveElemFromGroups
2844 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2845 //=======================================================================
2846 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2847                                              SMESHDS_Mesh *          aMesh)
2848 {
2849   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850   if (!groups.empty())
2851   {
2852     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2853     for (; GrIt != groups.end(); GrIt++)
2854     {
2855       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2856       if (!grp || grp->IsEmpty()) continue;
2857       grp->SMDSGroup().Remove(removeelem);
2858     }
2859   }
2860 }
2861
2862 //================================================================================
2863 /*!
2864  * \brief Replace elemToRm by elemToAdd in the all groups
2865  */
2866 //================================================================================
2867
2868 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2869                                             const SMDS_MeshElement* elemToAdd,
2870                                             SMESHDS_Mesh *          aMesh)
2871 {
2872   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2873   if (!groups.empty()) {
2874     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2875     for ( ; grIt != groups.end(); grIt++ ) {
2876       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2877       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2878         group->SMDSGroup().Add( elemToAdd );
2879     }
2880   }
2881 }
2882
2883 //================================================================================
2884 /*!
2885  * \brief Replace elemToRm by elemToAdd in the all groups
2886  */
2887 //================================================================================
2888
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2890                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2891                                             SMESHDS_Mesh *                         aMesh)
2892 {
2893   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894   if (!groups.empty())
2895   {
2896     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897     for ( ; grIt != groups.end(); grIt++ ) {
2898       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2900         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2901           group->SMDSGroup().Add( elemToAdd[ i ] );
2902     }
2903   }
2904 }
2905
2906 //=======================================================================
2907 //function : QuadToTri
2908 //purpose  : Cut quadrangles into triangles.
2909 //           theCrit is used to select a diagonal to cut
2910 //=======================================================================
2911
2912 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2913                                   const bool         the13Diag)
2914 {
2915   ClearLastCreated();
2916   myLastCreatedElems.reserve( theElems.size() * 2 );
2917
2918   SMESHDS_Mesh *       aMesh = GetMeshDS();
2919   Handle(Geom_Surface) surface;
2920   SMESH_MesherHelper   helper( *GetMesh() );
2921
2922   TIDSortedElemSet::iterator itElem;
2923   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2924   {
2925     const SMDS_MeshElement* elem = *itElem;
2926     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2927       continue;
2928
2929     if ( elem->NbNodes() == 4 ) {
2930       // retrieve element nodes
2931       const SMDS_MeshNode* aNodes [4];
2932       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2933       int i = 0;
2934       while ( itN->more() )
2935         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2936
2937       int aShapeId = FindShape( elem );
2938       const SMDS_MeshElement* newElem1 = 0;
2939       const SMDS_MeshElement* newElem2 = 0;
2940       if ( the13Diag ) {
2941         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2942         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2943       }
2944       else {
2945         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2946         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2947       }
2948       myLastCreatedElems.push_back(newElem1);
2949       myLastCreatedElems.push_back(newElem2);
2950       // put a new triangle on the same shape and add to the same groups
2951       if ( aShapeId )
2952       {
2953         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2954         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2955       }
2956       AddToSameGroups( newElem1, elem, aMesh );
2957       AddToSameGroups( newElem2, elem, aMesh );
2958       aMesh->RemoveElement( elem );
2959     }
2960
2961     // Quadratic quadrangle
2962
2963     else if ( elem->NbNodes() >= 8 )
2964     {
2965       // get surface elem is on
2966       int aShapeId = FindShape( elem );
2967       if ( aShapeId != helper.GetSubShapeID() ) {
2968         surface.Nullify();
2969         TopoDS_Shape shape;
2970         if ( aShapeId > 0 )
2971           shape = aMesh->IndexToShape( aShapeId );
2972         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2973           TopoDS_Face face = TopoDS::Face( shape );
2974           surface = BRep_Tool::Surface( face );
2975           if ( !surface.IsNull() )
2976             helper.SetSubShape( shape );
2977         }
2978       }
2979
2980       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2981       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2982       for ( int i = 0; itN->more(); ++i )
2983         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2984
2985       const SMDS_MeshNode* centrNode = aNodes[8];
2986       if ( centrNode == 0 )
2987       {
2988         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2989                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2990                                            surface.IsNull() );
2991         myLastCreatedNodes.push_back(centrNode);
2992       }
2993
2994       // create a new element
2995       const SMDS_MeshElement* newElem1 = 0;
2996       const SMDS_MeshElement* newElem2 = 0;
2997       if ( the13Diag ) {
2998         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2999                                   aNodes[6], aNodes[7], centrNode );
3000         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3001                                   centrNode, aNodes[4], aNodes[5] );
3002       }
3003       else {
3004         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3005                                   aNodes[7], aNodes[4], centrNode );
3006         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3007                                   centrNode, aNodes[5], aNodes[6] );
3008       }
3009       myLastCreatedElems.push_back(newElem1);
3010       myLastCreatedElems.push_back(newElem2);
3011       // put a new triangle on the same shape and add to the same groups
3012       if ( aShapeId )
3013       {
3014         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3015         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3016       }
3017       AddToSameGroups( newElem1, elem, aMesh );
3018       AddToSameGroups( newElem2, elem, aMesh );
3019       aMesh->RemoveElement( elem );
3020     }
3021   }
3022
3023   return true;
3024 }
3025
3026 //=======================================================================
3027 //function : getAngle
3028 //purpose  :
3029 //=======================================================================
3030
3031 double getAngle(const SMDS_MeshElement * tr1,
3032                 const SMDS_MeshElement * tr2,
3033                 const SMDS_MeshNode *    n1,
3034                 const SMDS_MeshNode *    n2)
3035 {
3036   double angle = 2. * M_PI; // bad angle
3037
3038   // get normals
3039   SMESH::Controls::TSequenceOfXYZ P1, P2;
3040   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3041        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3042     return angle;
3043   gp_Vec N1,N2;
3044   if(!tr1->IsQuadratic())
3045     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3046   else
3047     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3048   if ( N1.SquareMagnitude() <= gp::Resolution() )
3049     return angle;
3050   if(!tr2->IsQuadratic())
3051     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3052   else
3053     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3054   if ( N2.SquareMagnitude() <= gp::Resolution() )
3055     return angle;
3056
3057   // find the first diagonal node n1 in the triangles:
3058   // take in account a diagonal link orientation
3059   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3060   for ( int t = 0; t < 2; t++ ) {
3061     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3062     int i = 0, iDiag = -1;
3063     while ( it->more()) {
3064       const SMDS_MeshElement *n = it->next();
3065       if ( n == n1 || n == n2 ) {
3066         if ( iDiag < 0)
3067           iDiag = i;
3068         else {
3069           if ( i - iDiag == 1 )
3070             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3071           else
3072             nFirst[ t ] = n;
3073           break;
3074         }
3075       }
3076       i++;
3077     }
3078   }
3079   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3080     N2.Reverse();
3081
3082   angle = N1.Angle( N2 );
3083   //SCRUTE( angle );
3084   return angle;
3085 }
3086
3087 // =================================================
3088 // class generating a unique ID for a pair of nodes
3089 // and able to return nodes by that ID
3090 // =================================================
3091 class LinkID_Gen {
3092 public:
3093
3094   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3095     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3096   {}
3097
3098   long GetLinkID (const SMDS_MeshNode * n1,
3099                   const SMDS_MeshNode * n2) const
3100   {
3101     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3102   }
3103
3104   bool GetNodes (const long             theLinkID,
3105                  const SMDS_MeshNode* & theNode1,
3106                  const SMDS_MeshNode* & theNode2) const
3107   {
3108     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3109     if ( !theNode1 ) return false;
3110     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3111     if ( !theNode2 ) return false;
3112     return true;
3113   }
3114
3115 private:
3116   LinkID_Gen();
3117   const SMESHDS_Mesh* myMesh;
3118   long                myMaxID;
3119 };
3120
3121
3122 //=======================================================================
3123 //function : TriToQuad
3124 //purpose  : Fuse neighbour triangles into quadrangles.
3125 //           theCrit is used to select a neighbour to fuse with.
3126 //           theMaxAngle is a max angle between element normals at which
3127 //           fusion is still performed.
3128 //=======================================================================
3129
3130 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3131                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3132                                   const double                         theMaxAngle)
3133 {
3134   ClearLastCreated();
3135   myLastCreatedElems.reserve( theElems.size() / 2 );
3136
3137   if ( !theCrit.get() )
3138     return false;
3139
3140   SMESHDS_Mesh * aMesh = GetMeshDS();
3141
3142   // Prepare data for algo: build
3143   // 1. map of elements with their linkIDs
3144   // 2. map of linkIDs with their elements
3145
3146   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3147   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3148   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3149   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3150
3151   TIDSortedElemSet::iterator itElem;
3152   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3153   {
3154     const SMDS_MeshElement* elem = *itElem;
3155     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3156     bool IsTria = ( elem->NbCornerNodes()==3 );
3157     if (!IsTria) continue;
3158
3159     // retrieve element nodes
3160     const SMDS_MeshNode* aNodes [4];
3161     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3162     int i = 0;
3163     while ( i < 3 )
3164       aNodes[ i++ ] = itN->next();
3165     aNodes[ 3 ] = aNodes[ 0 ];
3166
3167     // fill maps
3168     for ( i = 0; i < 3; i++ ) {
3169       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3170       // check if elements sharing a link can be fused
3171       itLE = mapLi_listEl.find( link );
3172       if ( itLE != mapLi_listEl.end() ) {
3173         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3174           continue;
3175         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3176         //if ( FindShape( elem ) != FindShape( elem2 ))
3177         //  continue; // do not fuse triangles laying on different shapes
3178         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3179           continue; // avoid making badly shaped quads
3180         (*itLE).second.push_back( elem );
3181       }
3182       else {
3183         mapLi_listEl[ link ].push_back( elem );
3184       }
3185       mapEl_setLi [ elem ].insert( link );
3186     }
3187   }
3188   // Clean the maps from the links shared by a sole element, ie
3189   // links to which only one element is bound in mapLi_listEl
3190
3191   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3192     int nbElems = (*itLE).second.size();
3193     if ( nbElems < 2  ) {
3194       const SMDS_MeshElement* elem = (*itLE).second.front();
3195       SMESH_TLink link = (*itLE).first;
3196       mapEl_setLi[ elem ].erase( link );
3197       if ( mapEl_setLi[ elem ].empty() )
3198         mapEl_setLi.erase( elem );
3199     }
3200   }
3201
3202   // Algo: fuse triangles into quadrangles
3203
3204   while ( ! mapEl_setLi.empty() ) {
3205     // Look for the start element:
3206     // the element having the least nb of shared links
3207     const SMDS_MeshElement* startElem = 0;
3208     int minNbLinks = 4;
3209     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3210       int nbLinks = (*itEL).second.size();
3211       if ( nbLinks < minNbLinks ) {
3212         startElem = (*itEL).first;
3213         minNbLinks = nbLinks;
3214         if ( minNbLinks == 1 )
3215           break;
3216       }
3217     }
3218
3219     // search elements to fuse starting from startElem or links of elements
3220     // fused earlyer - startLinks
3221     list< SMESH_TLink > startLinks;
3222     while ( startElem || !startLinks.empty() ) {
3223       while ( !startElem && !startLinks.empty() ) {
3224         // Get an element to start, by a link
3225         SMESH_TLink linkId = startLinks.front();
3226         startLinks.pop_front();
3227         itLE = mapLi_listEl.find( linkId );
3228         if ( itLE != mapLi_listEl.end() ) {
3229           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3230           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3231           for ( ; itE != listElem.end() ; itE++ )
3232             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3233               startElem = (*itE);
3234           mapLi_listEl.erase( itLE );
3235         }
3236       }
3237
3238       if ( startElem ) {
3239         // Get candidates to be fused
3240         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3241         const SMESH_TLink *link12 = 0, *link13 = 0;
3242         startElem = 0;
3243         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3244         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3245         ASSERT( !setLi.empty() );
3246         set< SMESH_TLink >::iterator itLi;
3247         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3248         {
3249           const SMESH_TLink & link = (*itLi);
3250           itLE = mapLi_listEl.find( link );
3251           if ( itLE == mapLi_listEl.end() )
3252             continue;
3253
3254           const SMDS_MeshElement* elem = (*itLE).second.front();
3255           if ( elem == tr1 )
3256             elem = (*itLE).second.back();
3257           mapLi_listEl.erase( itLE );
3258           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3259             continue;
3260           if ( tr2 ) {
3261             tr3 = elem;
3262             link13 = &link;
3263           }
3264           else {
3265             tr2 = elem;
3266             link12 = &link;
3267           }
3268
3269           // add other links of elem to list of links to re-start from
3270           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3271           set< SMESH_TLink >::iterator it;
3272           for ( it = links.begin(); it != links.end(); it++ ) {
3273             const SMESH_TLink& link2 = (*it);
3274             if ( link2 != link )
3275               startLinks.push_back( link2 );
3276           }
3277         }
3278
3279         // Get nodes of possible quadrangles
3280         const SMDS_MeshNode *n12 [4], *n13 [4];
3281         bool Ok12 = false, Ok13 = false;
3282         const SMDS_MeshNode *linkNode1, *linkNode2;
3283         if(tr2) {
3284           linkNode1 = link12->first;
3285           linkNode2 = link12->second;
3286           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3287             Ok12 = true;
3288         }
3289         if(tr3) {
3290           linkNode1 = link13->first;
3291           linkNode2 = link13->second;
3292           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3293             Ok13 = true;
3294         }
3295
3296         // Choose a pair to fuse
3297         if ( Ok12 && Ok13 ) {
3298           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3299           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3300           double aBadRate12 = getBadRate( &quad12, theCrit );
3301           double aBadRate13 = getBadRate( &quad13, theCrit );
3302           if (  aBadRate13 < aBadRate12 )
3303             Ok12 = false;
3304           else
3305             Ok13 = false;
3306         }
3307
3308         // Make quadrangles
3309         // and remove fused elems and remove links from the maps
3310         mapEl_setLi.erase( tr1 );
3311         if ( Ok12 )
3312         {
3313           mapEl_setLi.erase( tr2 );
3314           mapLi_listEl.erase( *link12 );
3315           if ( tr1->NbNodes() == 3 )
3316           {
3317             const SMDS_MeshElement* newElem = 0;
3318             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3319             myLastCreatedElems.push_back(newElem);
3320             AddToSameGroups( newElem, tr1, aMesh );
3321             int aShapeId = tr1->getshapeId();
3322             if ( aShapeId )
3323               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3324             aMesh->RemoveElement( tr1 );
3325             aMesh->RemoveElement( tr2 );
3326           }
3327           else {
3328             vector< const SMDS_MeshNode* > N1;
3329             vector< const SMDS_MeshNode* > N2;
3330             getNodesFromTwoTria(tr1,tr2,N1,N2);
3331             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3332             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3333             // i.e. first nodes from both arrays form a new diagonal
3334             const SMDS_MeshNode* aNodes[8];
3335             aNodes[0] = N1[0];
3336             aNodes[1] = N1[1];
3337             aNodes[2] = N2[0];
3338             aNodes[3] = N2[1];
3339             aNodes[4] = N1[3];
3340             aNodes[5] = N2[5];
3341             aNodes[6] = N2[3];
3342             aNodes[7] = N1[5];
3343             const SMDS_MeshElement* newElem = 0;
3344             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3345               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3346                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3347             else
3348               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3350             myLastCreatedElems.push_back(newElem);
3351             AddToSameGroups( newElem, tr1, aMesh );
3352             int aShapeId = tr1->getshapeId();
3353             if ( aShapeId )
3354               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3355             aMesh->RemoveElement( tr1 );
3356             aMesh->RemoveElement( tr2 );
3357             // remove middle node (9)
3358             if ( N1[4]->NbInverseElements() == 0 )
3359               aMesh->RemoveNode( N1[4] );
3360             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3361               aMesh->RemoveNode( N1[6] );
3362             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3363               aMesh->RemoveNode( N2[6] );
3364           }
3365         }
3366         else if ( Ok13 )
3367         {
3368           mapEl_setLi.erase( tr3 );
3369           mapLi_listEl.erase( *link13 );
3370           if ( tr1->NbNodes() == 3 ) {
3371             const SMDS_MeshElement* newElem = 0;
3372             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3373             myLastCreatedElems.push_back(newElem);
3374             AddToSameGroups( newElem, tr1, aMesh );
3375             int aShapeId = tr1->getshapeId();
3376             if ( aShapeId )
3377               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378             aMesh->RemoveElement( tr1 );
3379             aMesh->RemoveElement( tr3 );
3380           }
3381           else {
3382             vector< const SMDS_MeshNode* > N1;
3383             vector< const SMDS_MeshNode* > N2;
3384             getNodesFromTwoTria(tr1,tr3,N1,N2);
3385             // now we receive following N1 and N2 (using numeration as above image)
3386             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3387             // i.e. first nodes from both arrays form a new diagonal
3388             const SMDS_MeshNode* aNodes[8];
3389             aNodes[0] = N1[0];
3390             aNodes[1] = N1[1];
3391             aNodes[2] = N2[0];
3392             aNodes[3] = N2[1];
3393             aNodes[4] = N1[3];
3394             aNodes[5] = N2[5];
3395             aNodes[6] = N2[3];
3396             aNodes[7] = N1[5];
3397             const SMDS_MeshElement* newElem = 0;
3398             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3399               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3400                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3401             else
3402               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3404             myLastCreatedElems.push_back(newElem);
3405             AddToSameGroups( newElem, tr1, aMesh );
3406             int aShapeId = tr1->getshapeId();
3407             if ( aShapeId )
3408               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3409             aMesh->RemoveElement( tr1 );
3410             aMesh->RemoveElement( tr3 );
3411             // remove middle node (9)
3412             if ( N1[4]->NbInverseElements() == 0 )
3413               aMesh->RemoveNode( N1[4] );
3414             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3415               aMesh->RemoveNode( N1[6] );
3416             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3417               aMesh->RemoveNode( N2[6] );
3418           }
3419         }
3420
3421         // Next element to fuse: the rejected one
3422         if ( tr3 )
3423           startElem = Ok12 ? tr3 : tr2;
3424
3425       } // if ( startElem )
3426     } // while ( startElem || !startLinks.empty() )
3427   } // while ( ! mapEl_setLi.empty() )
3428
3429   return true;
3430 }
3431
3432 //================================================================================
3433 /*!
3434  * \brief Return nodes linked to the given one
3435  * \param theNode - the node
3436  * \param linkedNodes - the found nodes
3437  * \param type - the type of elements to check
3438  *
3439  * Medium nodes are ignored
3440  */
3441 //================================================================================
3442
3443 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3444                                        TIDSortedElemSet &   linkedNodes,
3445                                        SMDSAbs_ElementType  type )
3446 {
3447   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3448   while ( elemIt->more() )
3449   {
3450     const SMDS_MeshElement* elem = elemIt->next();
3451     if(elem->GetType() == SMDSAbs_0DElement)
3452       continue;
3453
3454     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3455     if ( elem->GetType() == SMDSAbs_Volume )
3456     {
3457       SMDS_VolumeTool vol( elem );
3458       while ( nodeIt->more() ) {
3459         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3460         if ( theNode != n && vol.IsLinked( theNode, n ))
3461           linkedNodes.insert( n );
3462       }
3463     }
3464     else
3465     {
3466       for ( int i = 0; nodeIt->more(); ++i ) {
3467         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468         if ( n == theNode ) {
3469           int iBefore = i - 1;
3470           int iAfter  = i + 1;
3471           if ( elem->IsQuadratic() ) {
3472             int nb = elem->NbNodes() / 2;
3473             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3474             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3475           }
3476           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3477           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3478         }
3479       }
3480     }
3481   }
3482 }
3483
3484 //=======================================================================
3485 //function : laplacianSmooth
3486 //purpose  : pulls theNode toward the center of surrounding nodes directly
3487 //           connected to that node along an element edge
3488 //=======================================================================
3489
3490 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3491                      const Handle(Geom_Surface)&          theSurface,
3492                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3493 {
3494   // find surrounding nodes
3495
3496   TIDSortedElemSet nodeSet;
3497   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3498
3499   // compute new coodrs
3500
3501   double coord[] = { 0., 0., 0. };
3502   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3503   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3504     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3505     if ( theSurface.IsNull() ) { // smooth in 3D
3506       coord[0] += node->X();
3507       coord[1] += node->Y();
3508       coord[2] += node->Z();
3509     }
3510     else { // smooth in 2D
3511       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3512       gp_XY* uv = theUVMap[ node ];
3513       coord[0] += uv->X();
3514       coord[1] += uv->Y();
3515     }
3516   }
3517   int nbNodes = nodeSet.size();
3518   if ( !nbNodes )
3519     return;
3520   coord[0] /= nbNodes;
3521   coord[1] /= nbNodes;
3522
3523   if ( !theSurface.IsNull() ) {
3524     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3525     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3526     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3527     coord[0] = p3d.X();
3528     coord[1] = p3d.Y();
3529     coord[2] = p3d.Z();
3530   }
3531   else
3532     coord[2] /= nbNodes;
3533
3534   // move node
3535
3536   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3537 }
3538
3539 //=======================================================================
3540 //function : centroidalSmooth
3541 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3542 //           surrounding elements
3543 //=======================================================================
3544
3545 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3546                       const Handle(Geom_Surface)&          theSurface,
3547                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3548 {
3549   gp_XYZ aNewXYZ(0.,0.,0.);
3550   SMESH::Controls::Area anAreaFunc;
3551   double totalArea = 0.;
3552   int nbElems = 0;
3553
3554   // compute new XYZ
3555
3556   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3557   while ( elemIt->more() )
3558   {
3559     const SMDS_MeshElement* elem = elemIt->next();
3560     nbElems++;
3561
3562     gp_XYZ elemCenter(0.,0.,0.);
3563     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3564     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3565     int nn = elem->NbNodes();
3566     if(elem->IsQuadratic()) nn = nn/2;
3567     int i=0;
3568     //while ( itN->more() ) {
3569     while ( i<nn ) {
3570       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3571       i++;
3572       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3573       aNodePoints.push_back( aP );
3574       if ( !theSurface.IsNull() ) { // smooth in 2D
3575         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3576         gp_XY* uv = theUVMap[ aNode ];
3577         aP.SetCoord( uv->X(), uv->Y(), 0. );
3578       }
3579       elemCenter += aP;
3580     }
3581     double elemArea = anAreaFunc.GetValue( aNodePoints );
3582     totalArea += elemArea;
3583     elemCenter /= nn;
3584     aNewXYZ += elemCenter * elemArea;
3585   }
3586   aNewXYZ /= totalArea;
3587   if ( !theSurface.IsNull() ) {
3588     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3589     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3590   }
3591
3592   // move node
3593
3594   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3595 }
3596
3597 //=======================================================================
3598 //function : getClosestUV
3599 //purpose  : return UV of closest projection
3600 //=======================================================================
3601
3602 static bool getClosestUV (Extrema_GenExtPS& projector,
3603                           const gp_Pnt&     point,
3604                           gp_XY &           result)
3605 {
3606   projector.Perform( point );
3607   if ( projector.IsDone() ) {
3608     double u, v, minVal = DBL_MAX;
3609     for ( int i = projector.NbExt(); i > 0; i-- )
3610       if ( projector.SquareDistance( i ) < minVal ) {
3611         minVal = projector.SquareDistance( i );
3612         projector.Point( i ).Parameter( u, v );
3613       }
3614     result.SetCoord( u, v );
3615     return true;
3616   }
3617   return false;
3618 }
3619
3620 //=======================================================================
3621 //function : Smooth
3622 //purpose  : Smooth theElements during theNbIterations or until a worst
3623 //           element has aspect ratio <= theTgtAspectRatio.
3624 //           Aspect Ratio varies in range [1.0, inf].
3625 //           If theElements is empty, the whole mesh is smoothed.
3626 //           theFixedNodes contains additionally fixed nodes. Nodes built
3627 //           on edges and boundary nodes are always fixed.
3628 //=======================================================================
3629
3630 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3631                                set<const SMDS_MeshNode*> & theFixedNodes,
3632                                const SmoothMethod          theSmoothMethod,
3633                                const int                   theNbIterations,
3634                                double                      theTgtAspectRatio,
3635                                const bool                  the2D)
3636 {
3637   ClearLastCreated();
3638
3639   if ( theTgtAspectRatio < 1.0 )
3640     theTgtAspectRatio = 1.0;
3641
3642   const double disttol = 1.e-16;
3643
3644   SMESH::Controls::AspectRatio aQualityFunc;
3645
3646   SMESHDS_Mesh* aMesh = GetMeshDS();
3647
3648   if ( theElems.empty() ) {
3649     // add all faces to theElems
3650     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3651     while ( fIt->more() ) {
3652       const SMDS_MeshElement* face = fIt->next();
3653       theElems.insert( theElems.end(), face );
3654     }
3655   }
3656   // get all face ids theElems are on
3657   set< int > faceIdSet;
3658   TIDSortedElemSet::iterator itElem;
3659   if ( the2D )
3660     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3661       int fId = FindShape( *itElem );
3662       // check that corresponding submesh exists and a shape is face
3663       if (fId &&
3664           faceIdSet.find( fId ) == faceIdSet.end() &&
3665           aMesh->MeshElements( fId )) {
3666         TopoDS_Shape F = aMesh->IndexToShape( fId );
3667         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3668           faceIdSet.insert( fId );
3669       }
3670     }
3671   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3672
3673   // ===============================================
3674   // smooth elements on each TopoDS_Face separately
3675   // ===============================================
3676
3677   SMESH_MesherHelper helper( *GetMesh() );
3678
3679   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3680   for ( ; fId != faceIdSet.rend(); ++fId )
3681   {
3682     // get face surface and submesh
3683     Handle(Geom_Surface) surface;
3684     SMESHDS_SubMesh* faceSubMesh = 0;
3685     TopoDS_Face face;
3686     double fToler2 = 0;
3687     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3688     bool isUPeriodic = false, isVPeriodic = false;
3689     if ( *fId )
3690     {
3691       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3692       surface = BRep_Tool::Surface( face );
3693       faceSubMesh = aMesh->MeshElements( *fId );
3694       fToler2 = BRep_Tool::Tolerance( face );
3695       fToler2 *= fToler2 * 10.;
3696       isUPeriodic = surface->IsUPeriodic();
3697       // if ( isUPeriodic )
3698       //   surface->UPeriod();
3699       isVPeriodic = surface->IsVPeriodic();
3700       // if ( isVPeriodic )
3701       //   surface->VPeriod();
3702       surface->Bounds( u1, u2, v1, v2 );
3703       helper.SetSubShape( face );
3704     }
3705     // ---------------------------------------------------------
3706     // for elements on a face, find movable and fixed nodes and
3707     // compute UV for them
3708     // ---------------------------------------------------------
3709     bool checkBoundaryNodes = false;
3710     bool isQuadratic = false;
3711     set<const SMDS_MeshNode*> setMovableNodes;
3712     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3713     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3714     list< const SMDS_MeshElement* > elemsOnFace;
3715
3716     Extrema_GenExtPS projector;
3717     GeomAdaptor_Surface surfAdaptor;
3718     if ( !surface.IsNull() ) {
3719       surfAdaptor.Load( surface );
3720       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3721     }
3722     int nbElemOnFace = 0;
3723     itElem = theElems.begin();
3724     // loop on not yet smoothed elements: look for elems on a face
3725     while ( itElem != theElems.end() )
3726     {
3727       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3728         break; // all elements found
3729
3730       const SMDS_MeshElement* elem = *itElem;
3731       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3732            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3733         ++itElem;
3734         continue;
3735       }
3736       elemsOnFace.push_back( elem );
3737       theElems.erase( itElem++ );
3738       nbElemOnFace++;
3739
3740       if ( !isQuadratic )
3741         isQuadratic = elem->IsQuadratic();
3742
3743       // get movable nodes of elem
3744       const SMDS_MeshNode* node;
3745       SMDS_TypeOfPosition posType;
3746       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3747       int nn = 0, nbn =  elem->NbNodes();
3748       if(elem->IsQuadratic())
3749         nbn = nbn/2;
3750       while ( nn++ < nbn ) {
3751         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3752         const SMDS_PositionPtr& pos = node->GetPosition();
3753         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3754         if (posType != SMDS_TOP_EDGE &&
3755             posType != SMDS_TOP_VERTEX &&
3756             theFixedNodes.find( node ) == theFixedNodes.end())
3757         {
3758           // check if all faces around the node are on faceSubMesh
3759           // because a node on edge may be bound to face
3760           bool all = true;
3761           if ( faceSubMesh ) {
3762             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3763             while ( eIt->more() && all ) {
3764               const SMDS_MeshElement* e = eIt->next();
3765               all = faceSubMesh->Contains( e );
3766             }
3767           }
3768           if ( all )
3769             setMovableNodes.insert( node );
3770           else
3771             checkBoundaryNodes = true;
3772         }
3773         if ( posType == SMDS_TOP_3DSPACE )
3774           checkBoundaryNodes = true;
3775       }
3776
3777       if ( surface.IsNull() )
3778         continue;
3779
3780       // get nodes to check UV
3781       list< const SMDS_MeshNode* > uvCheckNodes;
3782       const SMDS_MeshNode* nodeInFace = 0;
3783       itN = elem->nodesIterator();
3784       nn = 0; nbn =  elem->NbNodes();
3785       if(elem->IsQuadratic())
3786         nbn = nbn/2;
3787       while ( nn++ < nbn ) {
3788         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789         if ( node->GetPosition()->GetDim() == 2 )
3790           nodeInFace = node;
3791         if ( uvMap.find( node ) == uvMap.end() )
3792           uvCheckNodes.push_back( node );
3793         // add nodes of elems sharing node
3794         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3795         //         while ( eIt->more() ) {
3796         //           const SMDS_MeshElement* e = eIt->next();
3797         //           if ( e != elem ) {
3798         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3799         //             while ( nIt->more() ) {
3800         //               const SMDS_MeshNode* n =
3801         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3802         //               if ( uvMap.find( n ) == uvMap.end() )
3803         //                 uvCheckNodes.push_back( n );
3804         //             }
3805         //           }
3806         //         }
3807       }
3808       // check UV on face
3809       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3810       for ( ; n != uvCheckNodes.end(); ++n ) {
3811         node = *n;
3812         gp_XY uv( 0, 0 );
3813         const SMDS_PositionPtr& pos = node->GetPosition();
3814         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3815         // get existing UV
3816         if ( pos )
3817         {
3818           bool toCheck = true;
3819           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3820         }
3821         // compute not existing UV
3822         bool project = ( posType == SMDS_TOP_3DSPACE );
3823         // double dist1 = DBL_MAX, dist2 = 0;
3824         // if ( posType != SMDS_TOP_3DSPACE ) {
3825         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3826         //   project = dist1 > fToler2;
3827         // }
3828         if ( project ) { // compute new UV
3829           gp_XY newUV;
3830           gp_Pnt pNode = SMESH_NodeXYZ( node );
3831           if ( !getClosestUV( projector, pNode, newUV )) {
3832             MESSAGE("Node Projection Failed " << node);
3833           }
3834           else {
3835             if ( isUPeriodic )
3836               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3837             if ( isVPeriodic )
3838               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3839             // check new UV
3840             // if ( posType != SMDS_TOP_3DSPACE )
3841             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3842             // if ( dist2 < dist1 )
3843             uv = newUV;
3844           }
3845         }
3846         // store UV in the map
3847         listUV.push_back( uv );
3848         uvMap.insert( make_pair( node, &listUV.back() ));
3849       }
3850     } // loop on not yet smoothed elements
3851
3852     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3853       checkBoundaryNodes = true;
3854
3855     // fix nodes on mesh boundary
3856
3857     if ( checkBoundaryNodes ) {
3858       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3859       map< SMESH_TLink, int >::iterator link_nb;
3860       // put all elements links to linkNbMap
3861       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3862       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3863         const SMDS_MeshElement* elem = (*elemIt);
3864         int nbn =  elem->NbCornerNodes();
3865         // loop on elem links: insert them in linkNbMap
3866         for ( int iN = 0; iN < nbn; ++iN ) {
3867           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3868           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3869           SMESH_TLink link( n1, n2 );
3870           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3871           link_nb->second++;
3872         }
3873       }
3874       // remove nodes that are in links encountered only once from setMovableNodes
3875       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3876         if ( link_nb->second == 1 ) {
3877           setMovableNodes.erase( link_nb->first.node1() );
3878           setMovableNodes.erase( link_nb->first.node2() );
3879         }
3880       }
3881     }
3882
3883     // -----------------------------------------------------
3884     // for nodes on seam edge, compute one more UV ( uvMap2 );
3885     // find movable nodes linked to nodes on seam and which
3886     // are to be smoothed using the second UV ( uvMap2 )
3887     // -----------------------------------------------------
3888
3889     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3890     if ( !surface.IsNull() ) {
3891       TopExp_Explorer eExp( face, TopAbs_EDGE );
3892       for ( ; eExp.More(); eExp.Next() ) {
3893         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3894         if ( !BRep_Tool::IsClosed( edge, face ))
3895           continue;
3896         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3897         if ( !sm ) continue;
3898         // find out which parameter varies for a node on seam
3899         double f,l;
3900         gp_Pnt2d uv1, uv2;
3901         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3902         if ( pcurve.IsNull() ) continue;
3903         uv1 = pcurve->Value( f );
3904         edge.Reverse();
3905         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3906         if ( pcurve.IsNull() ) continue;
3907         uv2 = pcurve->Value( f );
3908         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3909         // assure uv1 < uv2
3910         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3911           std::swap( uv1, uv2 );
3912         // get nodes on seam and its vertices
3913         list< const SMDS_MeshNode* > seamNodes;
3914         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3915         while ( nSeamIt->more() ) {
3916           const SMDS_MeshNode* node = nSeamIt->next();
3917           if ( !isQuadratic || !IsMedium( node ))
3918             seamNodes.push_back( node );
3919         }
3920         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3921         for ( ; vExp.More(); vExp.Next() ) {
3922           sm = aMesh->MeshElements( vExp.Current() );
3923           if ( sm ) {
3924             nSeamIt = sm->GetNodes();
3925             while ( nSeamIt->more() )
3926               seamNodes.push_back( nSeamIt->next() );
3927           }
3928         }
3929         // loop on nodes on seam
3930         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3931         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3932           const SMDS_MeshNode* nSeam = *noSeIt;
3933           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3934           if ( n_uv == uvMap.end() )
3935             continue;
3936           // set the first UV
3937           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3938           // set the second UV
3939           listUV.push_back( *n_uv->second );
3940           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3941           if ( uvMap2.empty() )
3942             uvMap2 = uvMap; // copy the uvMap contents
3943           uvMap2[ nSeam ] = &listUV.back();
3944
3945           // collect movable nodes linked to ones on seam in nodesNearSeam
3946           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3947           while ( eIt->more() ) {
3948             const SMDS_MeshElement* e = eIt->next();
3949             int nbUseMap1 = 0, nbUseMap2 = 0;
3950             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3951             int nn = 0, nbn =  e->NbNodes();
3952             if(e->IsQuadratic()) nbn = nbn/2;
3953             while ( nn++ < nbn )
3954             {
3955               const SMDS_MeshNode* n =
3956                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3957               if (n == nSeam ||
3958                   setMovableNodes.find( n ) == setMovableNodes.end() )
3959                 continue;
3960               // add only nodes being closer to uv2 than to uv1
3961               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3962               //              0.5 * ( n->Y() + nSeam->Y() ),
3963               //              0.5 * ( n->Z() + nSeam->Z() ));
3964               // gp_XY uv;
3965               // getClosestUV( projector, pMid, uv );
3966               double x = uvMap[ n ]->Coord( iPar );
3967               if ( Abs( uv1.Coord( iPar ) - x ) >
3968                    Abs( uv2.Coord( iPar ) - x )) {
3969                 nodesNearSeam.insert( n );
3970                 nbUseMap2++;
3971               }
3972               else
3973                 nbUseMap1++;
3974             }
3975             // for centroidalSmooth all element nodes must
3976             // be on one side of a seam
3977             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3978               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3979               nn = 0;
3980               while ( nn++ < nbn ) {
3981                 const SMDS_MeshNode* n =
3982                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3983                 setMovableNodes.erase( n );
3984               }
3985             }
3986           }
3987         } // loop on nodes on seam
3988       } // loop on edge of a face
3989     } // if ( !face.IsNull() )
3990
3991     if ( setMovableNodes.empty() ) {
3992       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3993       continue; // goto next face
3994     }
3995
3996     // -------------
3997     // SMOOTHING //
3998     // -------------
3999
4000     int it = -1;
4001     double maxRatio = -1., maxDisplacement = -1.;
4002     set<const SMDS_MeshNode*>::iterator nodeToMove;
4003     for ( it = 0; it < theNbIterations; it++ ) {
4004       maxDisplacement = 0.;
4005       nodeToMove = setMovableNodes.begin();
4006       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4007         const SMDS_MeshNode* node = (*nodeToMove);
4008         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4009
4010         // smooth
4011         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4012         if ( theSmoothMethod == LAPLACIAN )
4013           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4014         else
4015           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016
4017         // node displacement
4018         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4019         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4020         if ( aDispl > maxDisplacement )
4021           maxDisplacement = aDispl;
4022       }
4023       // no node movement => exit
4024       //if ( maxDisplacement < 1.e-16 ) {
4025       if ( maxDisplacement < disttol ) {
4026         MESSAGE("-- no node movement --");
4027         break;
4028       }
4029
4030       // check elements quality
4031       maxRatio  = 0;
4032       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4033       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4034         const SMDS_MeshElement* elem = (*elemIt);
4035         if ( !elem || elem->GetType() != SMDSAbs_Face )
4036           continue;
4037         SMESH::Controls::TSequenceOfXYZ aPoints;
4038         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4039           double aValue = aQualityFunc.GetValue( aPoints );
4040           if ( aValue > maxRatio )
4041             maxRatio = aValue;
4042         }
4043       }
4044       if ( maxRatio <= theTgtAspectRatio ) {
4045         //MESSAGE("-- quality achieved --");
4046         break;
4047       }
4048       if (it+1 == theNbIterations) {
4049         //MESSAGE("-- Iteration limit exceeded --");
4050       }
4051     } // smoothing iterations
4052
4053     // MESSAGE(" Face id: " << *fId <<
4054     //         " Nb iterstions: " << it <<
4055     //         " Displacement: " << maxDisplacement <<
4056     //         " Aspect Ratio " << maxRatio);
4057
4058     // ---------------------------------------
4059     // new nodes positions are computed,
4060     // record movement in DS and set new UV
4061     // ---------------------------------------
4062     nodeToMove = setMovableNodes.begin();
4063     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4064       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4065       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4066       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4067       if ( node_uv != uvMap.end() ) {
4068         gp_XY* uv = node_uv->second;
4069         node->SetPosition
4070           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4071       }
4072     }
4073
4074     // move medium nodes of quadratic elements
4075     if ( isQuadratic )
4076     {
4077       vector<const SMDS_MeshNode*> nodes;
4078       bool checkUV;
4079       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4080       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4081       {
4082         const SMDS_MeshElement* QF = *elemIt;
4083         if ( QF->IsQuadratic() )
4084         {
4085           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4086                         SMDS_MeshElement::iterator() );
4087           nodes.push_back( nodes[0] );
4088           gp_Pnt xyz;
4089           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4090           {
4091             if ( !surface.IsNull() )
4092             {
4093               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4094               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4095               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4096               xyz = surface->Value( uv.X(), uv.Y() );
4097             }
4098             else {
4099               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4100             }
4101             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4102               // we have to move a medium node
4103               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4104           }
4105         }
4106       }
4107     }
4108
4109   } // loop on face ids
4110
4111 }
4112
4113 namespace
4114 {
4115   //=======================================================================
4116   //function : isReverse
4117   //purpose  : Return true if normal of prevNodes is not co-directied with
4118   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4119   //           iNotSame is where prevNodes and nextNodes are different.
4120   //           If result is true then future volume orientation is OK
4121   //=======================================================================
4122
4123   bool isReverse(const SMDS_MeshElement*             face,
4124                  const vector<const SMDS_MeshNode*>& prevNodes,
4125                  const vector<const SMDS_MeshNode*>& nextNodes,
4126                  const int                           iNotSame)
4127   {
4128
4129     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4130     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4131     gp_XYZ extrDir( pN - pP ), faceNorm;
4132     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4133
4134     return faceNorm * extrDir < 0.0;
4135   }
4136
4137   //================================================================================
4138   /*!
4139    * \brief Assure that theElemSets[0] holds elements, not nodes
4140    */
4141   //================================================================================
4142
4143   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4144   {
4145     if ( !theElemSets[0].empty() &&
4146          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4147     {
4148       std::swap( theElemSets[0], theElemSets[1] );
4149     }
4150     else if ( !theElemSets[1].empty() &&
4151               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4152     {
4153       std::swap( theElemSets[0], theElemSets[1] );
4154     }
4155   }
4156 }
4157
4158 //=======================================================================
4159 /*!
4160  * \brief Create elements by sweeping an element
4161  * \param elem - element to sweep
4162  * \param newNodesItVec - nodes generated from each node of the element
4163  * \param newElems - generated elements
4164  * \param nbSteps - number of sweeping steps
4165  * \param srcElements - to append elem for each generated element
4166  */
4167 //=======================================================================
4168
4169 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4170                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4171                                     list<const SMDS_MeshElement*>&        newElems,
4172                                     const size_t                          nbSteps,
4173                                     SMESH_SequenceOfElemPtr&              srcElements)
4174 {
4175   SMESHDS_Mesh* aMesh = GetMeshDS();
4176
4177   const int           nbNodes = elem->NbNodes();
4178   const int         nbCorners = elem->NbCornerNodes();
4179   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4180                                                           polyhedron creation !!! */
4181   // Loop on elem nodes:
4182   // find new nodes and detect same nodes indices
4183   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4184   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4185   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4186   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4187
4188   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4189   vector<int> sames(nbNodes);
4190   vector<bool> isSingleNode(nbNodes);
4191
4192   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4194     const SMDS_MeshNode*                         node = nnIt->first;
4195     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4196     if ( listNewNodes.empty() )
4197       return;
4198
4199     itNN   [ iNode ] = listNewNodes.begin();
4200     prevNod[ iNode ] = node;
4201     nextNod[ iNode ] = listNewNodes.front();
4202
4203     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4204                                                              corner node of linear */
4205     if ( prevNod[ iNode ] != nextNod [ iNode ])
4206       nbDouble += !isSingleNode[iNode];
4207
4208     if( iNode < nbCorners ) { // check corners only
4209       if ( prevNod[ iNode ] == nextNod [ iNode ])
4210         sames[nbSame++] = iNode;
4211       else
4212         iNotSameNode = iNode;
4213     }
4214   }
4215
4216   if ( nbSame == nbNodes || nbSame > 2) {
4217     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4218     return;
4219   }
4220
4221   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4222   {
4223     // fix nodes order to have bottom normal external
4224     if ( baseType == SMDSEntity_Polygon )
4225     {
4226       std::reverse( itNN.begin(), itNN.end() );
4227       std::reverse( prevNod.begin(), prevNod.end() );
4228       std::reverse( midlNod.begin(), midlNod.end() );
4229       std::reverse( nextNod.begin(), nextNod.end() );
4230       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4231     }
4232     else
4233     {
4234       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4235       SMDS_MeshCell::applyInterlace( ind, itNN );
4236       SMDS_MeshCell::applyInterlace( ind, prevNod );
4237       SMDS_MeshCell::applyInterlace( ind, nextNod );
4238       SMDS_MeshCell::applyInterlace( ind, midlNod );
4239       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4240       if ( nbSame > 0 )
4241       {
4242         sames[nbSame] = iNotSameNode;
4243         for ( int j = 0; j <= nbSame; ++j )
4244           for ( size_t i = 0; i < ind.size(); ++i )
4245             if ( ind[i] == sames[j] )
4246             {
4247               sames[j] = i;
4248               break;
4249             }
4250         iNotSameNode = sames[nbSame];
4251       }
4252     }
4253   }
4254   else if ( elem->GetType() == SMDSAbs_Edge )
4255   {
4256     // orient a new face same as adjacent one
4257     int i1, i2;
4258     const SMDS_MeshElement* e;
4259     TIDSortedElemSet dummy;
4260     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4261         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4262         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4263     {
4264       // there is an adjacent face, check order of nodes in it
4265       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4266       if ( sameOrder )
4267       {
4268         std::swap( itNN[0],    itNN[1] );
4269         std::swap( prevNod[0], prevNod[1] );
4270         std::swap( nextNod[0], nextNod[1] );
4271         std::swap( isSingleNode[0], isSingleNode[1] );
4272         if ( nbSame > 0 )
4273           sames[0] = 1 - sames[0];
4274         iNotSameNode = 1 - iNotSameNode;
4275       }
4276     }
4277   }
4278
4279   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4280   if ( nbSame > 0 ) {
4281     iSameNode    = sames[ nbSame-1 ];
4282     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4283     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4284     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4285   }
4286
4287   if ( baseType == SMDSEntity_Polygon )
4288   {
4289     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4290     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4291   }
4292   else if ( baseType == SMDSEntity_Quad_Polygon )
4293   {
4294     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4295     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4296   }
4297
4298   // make new elements
4299   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4300   {
4301     // get next nodes
4302     for ( iNode = 0; iNode < nbNodes; iNode++ )
4303     {
4304       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4305       nextNod[ iNode ] = *itNN[ iNode ]++;
4306     }
4307
4308     SMDS_MeshElement* aNewElem = 0;
4309     /*if(!elem->IsPoly())*/ {
4310       switch ( baseType ) {
4311       case SMDSEntity_0D:
4312       case SMDSEntity_Node: { // sweep NODE
4313         if ( nbSame == 0 ) {
4314           if ( isSingleNode[0] )
4315             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4316           else
4317             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4318         }
4319         else
4320           return;
4321         break;
4322       }
4323       case SMDSEntity_Edge: { // sweep EDGE
4324         if ( nbDouble == 0 )
4325         {
4326           if ( nbSame == 0 ) // ---> quadrangle
4327             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4328                                       nextNod[ 1 ], nextNod[ 0 ] );
4329           else               // ---> triangle
4330             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331                                       nextNod[ iNotSameNode ] );
4332         }
4333         else                 // ---> polygon
4334         {
4335           vector<const SMDS_MeshNode*> poly_nodes;
4336           poly_nodes.push_back( prevNod[0] );
4337           poly_nodes.push_back( prevNod[1] );
4338           if ( prevNod[1] != nextNod[1] )
4339           {
4340             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4341             poly_nodes.push_back( nextNod[1] );
4342           }
4343           if ( prevNod[0] != nextNod[0] )
4344           {
4345             poly_nodes.push_back( nextNod[0] );
4346             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4347           }
4348           switch ( poly_nodes.size() ) {
4349           case 3:
4350             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4351             break;
4352           case 4:
4353             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4354                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4355             break;
4356           default:
4357             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4358           }
4359         }
4360         break;
4361       }
4362       case SMDSEntity_Triangle: // TRIANGLE --->
4363       {
4364         if ( nbDouble > 0 ) break;
4365         if ( nbSame == 0 )       // ---> pentahedron
4366           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4367                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4368
4369         else if ( nbSame == 1 )  // ---> pyramid
4370           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4371                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4372                                        nextNod[ iSameNode ]);
4373
4374         else // 2 same nodes:       ---> tetrahedron
4375           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4376                                        nextNod[ iNotSameNode ]);
4377         break;
4378       }
4379       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4380       {
4381         if ( nbSame == 2 )
4382           return;
4383         if ( nbDouble+nbSame == 2 )
4384         {
4385           if(nbSame==0) {      // ---> quadratic quadrangle
4386             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4387                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4388           }
4389           else { //(nbSame==1) // ---> quadratic triangle
4390             if(sames[0]==2) {
4391               return; // medium node on axis
4392             }
4393             else if(sames[0]==0)
4394               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4395                                         prevNod[2], midlNod[1], nextNod[2] );
4396             else // sames[0]==1
4397               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4398                                         prevNod[2], nextNod[2], midlNod[0]);
4399           }
4400         }
4401         else if ( nbDouble == 3 )
4402         {
4403           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4404             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4405                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4406           }
4407         }
4408         else
4409           return;
4410         break;
4411       }
4412       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4413         if ( nbDouble > 0 ) break;
4414
4415         if ( nbSame == 0 )       // ---> hexahedron
4416           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4417                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4418
4419         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4420           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4421                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4422                                        nextNod[ iSameNode ]);
4423           newElems.push_back( aNewElem );
4424           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4425                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4426                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4427         }
4428         else if ( nbSame == 2 ) { // ---> pentahedron
4429           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4430             // iBeforeSame is same too
4431             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4432                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4433                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4434           else
4435             // iAfterSame is same too
4436             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4437                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4438                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4439         }
4440         break;
4441       }
4442       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4443       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4444         if ( nbDouble+nbSame != 3 ) break;
4445         if(nbSame==0) {
4446           // --->  pentahedron with 15 nodes
4447           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4448                                        nextNod[0], nextNod[1], nextNod[2],
4449                                        prevNod[3], prevNod[4], prevNod[5],
4450                                        nextNod[3], nextNod[4], nextNod[5],
4451                                        midlNod[0], midlNod[1], midlNod[2]);
4452         }
4453         else if(nbSame==1) {
4454           // --->  2d order pyramid of 13 nodes
4455           int apex = iSameNode;
4456           int i0 = ( apex + 1 ) % nbCorners;
4457           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4458           int i0a = apex + 3;
4459           int i1a = i1 + 3;
4460           int i01 = i0 + 3;
4461           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4462                                       nextNod[i0], nextNod[i1], prevNod[apex],
4463                                       prevNod[i01], midlNod[i0],
4464                                       nextNod[i01], midlNod[i1],
4465                                       prevNod[i1a], prevNod[i0a],
4466                                       nextNod[i0a], nextNod[i1a]);
4467         }
4468         else if(nbSame==2) {
4469           // --->  2d order tetrahedron of 10 nodes
4470           int n1 = iNotSameNode;
4471           int n2 = ( n1 + 1             ) % nbCorners;
4472           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4473           int n12 = n1 + 3;
4474           int n23 = n2 + 3;
4475           int n31 = n3 + 3;
4476           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4477                                        prevNod[n12], prevNod[n23], prevNod[n31],
4478                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4479         }
4480         break;
4481       }
4482       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4483         if( nbSame == 0 ) {
4484           if ( nbDouble != 4 ) break;
4485           // --->  hexahedron with 20 nodes
4486           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4487                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4488                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4489                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4490                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4491         }
4492         else if(nbSame==1) {
4493           // ---> pyramid + pentahedron - can not be created since it is needed
4494           // additional middle node at the center of face
4495           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4496           return;
4497         }
4498         else if( nbSame == 2 ) {
4499           if ( nbDouble != 2 ) break;
4500           // --->  2d order Pentahedron with 15 nodes
4501           int n1,n2,n4,n5;
4502           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4503             // iBeforeSame is same too
4504             n1 = iBeforeSame;
4505             n2 = iOpposSame;
4506             n4 = iSameNode;
4507             n5 = iAfterSame;
4508           }
4509           else {
4510             // iAfterSame is same too
4511             n1 = iSameNode;
4512             n2 = iBeforeSame;
4513             n4 = iAfterSame;
4514             n5 = iOpposSame;
4515           }
4516           int n12 = n2 + 4;
4517           int n45 = n4 + 4;
4518           int n14 = n1 + 4;
4519           int n25 = n5 + 4;
4520           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4521                                        prevNod[n4], prevNod[n5], nextNod[n5],
4522                                        prevNod[n12], midlNod[n2], nextNod[n12],
4523                                        prevNod[n45], midlNod[n5], nextNod[n45],
4524                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4525         }
4526         break;
4527       }
4528       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4529
4530         if( nbSame == 0 && nbDouble == 9 ) {
4531           // --->  tri-quadratic hexahedron with 27 nodes
4532           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4533                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4534                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4535                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4536                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4537                                        prevNod[8], // bottom center
4538                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4539                                        nextNod[8], // top center
4540                                        midlNod[8]);// elem center
4541         }
4542         else
4543         {
4544           return;
4545         }
4546         break;
4547       }
4548       case SMDSEntity_Polygon: { // sweep POLYGON
4549
4550         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4551           // --->  hexagonal prism
4552           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4553                                        prevNod[3], prevNod[4], prevNod[5],
4554                                        nextNod[0], nextNod[1], nextNod[2],
4555                                        nextNod[3], nextNod[4], nextNod[5]);
4556         }
4557         break;
4558       }
4559       case SMDSEntity_Ball:
4560         return;
4561
4562       default:
4563         break;
4564       } // switch ( baseType )
4565     } // scope
4566
4567     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4568     {
4569       if ( baseType != SMDSEntity_Polygon )
4570       {
4571         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4572         SMDS_MeshCell::applyInterlace( ind, prevNod );
4573         SMDS_MeshCell::applyInterlace( ind, nextNod );
4574         SMDS_MeshCell::applyInterlace( ind, midlNod );
4575         SMDS_MeshCell::applyInterlace( ind, itNN );
4576         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4577         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4578       }
4579       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4580       vector<int> quantities (nbNodes + 2);
4581       polyedre_nodes.clear();
4582       quantities.clear();
4583
4584       // bottom of prism
4585       for (int inode = 0; inode < nbNodes; inode++)
4586         polyedre_nodes.push_back( prevNod[inode] );
4587       quantities.push_back( nbNodes );
4588
4589       // top of prism
4590       polyedre_nodes.push_back( nextNod[0] );
4591       for (int inode = nbNodes; inode-1; --inode )
4592         polyedre_nodes.push_back( nextNod[inode-1] );
4593       quantities.push_back( nbNodes );
4594
4595       // side faces
4596       // 3--6--2
4597       // |     |
4598       // 7     5
4599       // |     |
4600       // 0--4--1
4601       const int iQuad = elem->IsQuadratic();
4602       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4603       {
4604         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4605         int inextface = (iface+1+iQuad) % nbNodes;
4606         int imid      = (iface+1) % nbNodes;
4607         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4608         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4609         polyedre_nodes.push_back( prevNod[iface] );             // 1
4610         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4611         {
4612           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4613           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4614         }
4615         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4616         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4617         {
4618           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4619           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4620         }
4621         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4622         if ( nbFaceNodes > 2 )
4623           quantities.push_back( nbFaceNodes );
4624         else // degenerated face
4625           polyedre_nodes.resize( prevNbNodes );
4626       }
4627       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4628
4629     } // try to create a polyherdal prism
4630
4631     if ( aNewElem ) {
4632       newElems.push_back( aNewElem );
4633       myLastCreatedElems.push_back(aNewElem);
4634       srcElements.push_back( elem );
4635     }
4636
4637     // set new prev nodes
4638     for ( iNode = 0; iNode < nbNodes; iNode++ )
4639       prevNod[ iNode ] = nextNod[ iNode ];
4640
4641   } // loop on steps
4642 }
4643
4644 //=======================================================================
4645 /*!
4646  * \brief Create 1D and 2D elements around swept elements
4647  * \param mapNewNodes - source nodes and ones generated from them
4648  * \param newElemsMap - source elements and ones generated from them
4649  * \param elemNewNodesMap - nodes generated from each node of each element
4650  * \param elemSet - all swept elements
4651  * \param nbSteps - number of sweeping steps
4652  * \param srcElements - to append elem for each generated element
4653  */
4654 //=======================================================================
4655
4656 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4657                                   TTElemOfElemListMap &    newElemsMap,
4658                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4659                                   TIDSortedElemSet&        elemSet,
4660                                   const int                nbSteps,
4661                                   SMESH_SequenceOfElemPtr& srcElements)
4662 {
4663   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4664   SMESHDS_Mesh* aMesh = GetMeshDS();
4665
4666   // Find nodes belonging to only one initial element - sweep them into edges.
4667
4668   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4669   for ( ; nList != mapNewNodes.end(); nList++ )
4670   {
4671     const SMDS_MeshNode* node =
4672       static_cast<const SMDS_MeshNode*>( nList->first );
4673     if ( newElemsMap.count( node ))
4674       continue; // node was extruded into edge
4675     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4676     int nbInitElems = 0;
4677     const SMDS_MeshElement* el = 0;
4678     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4679     while ( eIt->more() && nbInitElems < 2 ) {
4680       const SMDS_MeshElement* e = eIt->next();
4681       SMDSAbs_ElementType  type = e->GetType();
4682       if ( type == SMDSAbs_Volume ||
4683            type < highType ||
4684            !elemSet.count(e))
4685         continue;
4686       if ( type > highType ) {
4687         nbInitElems = 0;
4688         highType    = type;
4689       }
4690       el = e;
4691       ++nbInitElems;
4692     }
4693     if ( nbInitElems == 1 ) {
4694       bool NotCreateEdge = el && el->IsMediumNode(node);
4695       if(!NotCreateEdge) {
4696         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4697         list<const SMDS_MeshElement*> newEdges;
4698         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4699       }
4700     }
4701   }
4702
4703   // Make a ceiling for each element ie an equal element of last new nodes.
4704   // Find free links of faces - make edges and sweep them into faces.
4705
4706   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4707
4708   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4709   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4710   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4711   {
4712     const SMDS_MeshElement* elem = itElem->first;
4713     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4714
4715     if(itElem->second.size()==0) continue;
4716
4717     const bool isQuadratic = elem->IsQuadratic();
4718
4719     if ( elem->GetType() == SMDSAbs_Edge ) {
4720       // create a ceiling edge
4721       if ( !isQuadratic ) {
4722         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4723                                vecNewNodes[ 1 ]->second.back())) {
4724           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4725                                                       vecNewNodes[ 1 ]->second.back()));
4726           srcElements.push_back( elem );
4727         }
4728       }
4729       else {
4730         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4731                                vecNewNodes[ 1 ]->second.back(),
4732                                vecNewNodes[ 2 ]->second.back())) {
4733           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4734                                                       vecNewNodes[ 1 ]->second.back(),
4735                                                       vecNewNodes[ 2 ]->second.back()));
4736           srcElements.push_back( elem );
4737         }
4738       }
4739     }
4740     if ( elem->GetType() != SMDSAbs_Face )
4741       continue;
4742
4743     bool hasFreeLinks = false;
4744
4745     TIDSortedElemSet avoidSet;
4746     avoidSet.insert( elem );
4747
4748     set<const SMDS_MeshNode*> aFaceLastNodes;
4749     int iNode, nbNodes = vecNewNodes.size();
4750     if ( !isQuadratic ) {
4751       // loop on the face nodes
4752       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4753         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4754         // look for free links of the face
4755         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4756         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4757         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4758         // check if a link n1-n2 is free
4759         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4760           hasFreeLinks = true;
4761           // make a new edge and a ceiling for a new edge
4762           const SMDS_MeshElement* edge;
4763           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4764             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4765             srcElements.push_back( myLastCreatedElems.back() );
4766           }
4767           n1 = vecNewNodes[ iNode ]->second.back();
4768           n2 = vecNewNodes[ iNext ]->second.back();
4769           if ( !aMesh->FindEdge( n1, n2 )) {
4770             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4771             srcElements.push_back( edge );
4772           }
4773         }
4774       }
4775     }
4776     else { // elem is quadratic face
4777       int nbn = nbNodes/2;
4778       for ( iNode = 0; iNode < nbn; iNode++ ) {
4779         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4780         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4781         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4782         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4783         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4784         // check if a link is free
4785         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4786              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4787              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4788           hasFreeLinks = true;
4789           // make an edge and a ceiling for a new edge
4790           // find medium node
4791           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4792             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4793             srcElements.push_back( elem );
4794           }
4795           n1 = vecNewNodes[ iNode ]->second.back();
4796           n2 = vecNewNodes[ iNext ]->second.back();
4797           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4798           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4799             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4800             srcElements.push_back( elem );
4801           }
4802         }
4803       }
4804       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4805         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4806       }
4807     }
4808
4809     // sweep free links into faces
4810
4811     if ( hasFreeLinks ) {
4812       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4813       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4814
4815       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4816       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4817       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4818         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4819         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4820       }
4821       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4822         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4823         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4824       }
4825       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4826         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4827         std::advance( v, volNb );
4828         // find indices of free faces of a volume and their source edges
4829         list< int > freeInd;
4830         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4831         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4832         int iF, nbF = vTool.NbFaces();
4833         for ( iF = 0; iF < nbF; iF ++ ) {
4834           if ( vTool.IsFreeFace( iF ) &&
4835                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4836                initNodeSet != faceNodeSet) // except an initial face
4837           {
4838             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4839               continue;
4840             if ( faceNodeSet == initNodeSetNoCenter )
4841               continue;
4842             freeInd.push_back( iF );
4843             // find source edge of a free face iF
4844             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4845             vector<const SMDS_MeshNode*>::iterator lastCommom;
4846             commonNodes.resize( nbNodes, 0 );
4847             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4848                                                 initNodeSet.begin(), initNodeSet.end(),
4849                                                 commonNodes.begin());
4850             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4851               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4852             else
4853               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4854 #ifdef _DEBUG_
4855             if ( !srcEdges.back() )
4856             {
4857               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4858                    << iF << " of volume #" << vTool.ID() << endl;
4859             }
4860 #endif
4861           }
4862         }
4863         if ( freeInd.empty() )
4864           continue;
4865
4866         // create wall faces for all steps;
4867         // if such a face has been already created by sweep of edge,
4868         // assure that its orientation is OK
4869         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4870         {
4871           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4872           vTool.SetExternalNormal();
4873           const int nextShift = vTool.IsForward() ? +1 : -1;
4874           list< int >::iterator ind = freeInd.begin();
4875           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4876           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4877           {
4878             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4879             int nbn = vTool.NbFaceNodes( *ind );
4880             const SMDS_MeshElement * f = 0;
4881             if ( nbn == 3 )              ///// triangle
4882             {
4883               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4884               if ( !f ||
4885                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4886               {
4887                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4888                                                      nodes[ 1 ],
4889                                                      nodes[ 1 + nextShift ] };
4890                 if ( f )
4891                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4892                 else
4893                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4894                                                                newOrder[ 2 ] ));
4895               }
4896             }
4897             else if ( nbn == 4 )       ///// quadrangle
4898             {
4899               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4900               if ( !f ||
4901                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4902               {
4903                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4904                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4909                                                                newOrder[ 2 ], newOrder[ 3 ]));
4910               }
4911             }
4912             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4913             {
4914               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4915               if ( !f ||
4916                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4917               {
4918                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4919                                                      nodes[2],
4920                                                      nodes[2 + 2*nextShift],
4921                                                      nodes[3 - 2*nextShift],
4922                                                      nodes[3],
4923                                                      nodes[3 + 2*nextShift]};
4924                 if ( f )
4925                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4926                 else
4927                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4928                                                                newOrder[ 1 ],
4929                                                                newOrder[ 2 ],
4930                                                                newOrder[ 3 ],
4931                                                                newOrder[ 4 ],
4932                                                                newOrder[ 5 ] ));
4933               }
4934             }
4935             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4936             {
4937               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4938                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4939               if ( !f ||
4940                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4943                                                      nodes[4 - 2*nextShift],
4944                                                      nodes[4],
4945                                                      nodes[4 + 2*nextShift],
4946                                                      nodes[1],
4947                                                      nodes[5 - 2*nextShift],
4948                                                      nodes[5],
4949                                                      nodes[5 + 2*nextShift] };
4950                 if ( f )
4951                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4952                 else
4953                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4954                                                               newOrder[ 2 ], newOrder[ 3 ],
4955                                                               newOrder[ 4 ], newOrder[ 5 ],
4956                                                               newOrder[ 6 ], newOrder[ 7 ]));
4957               }
4958             }
4959             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4960             {
4961               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4962                                       SMDSAbs_Face, /*noMedium=*/false);
4963               if ( !f ||
4964                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4965               {
4966                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4967                                                      nodes[4 - 2*nextShift],
4968                                                      nodes[4],
4969                                                      nodes[4 + 2*nextShift],
4970                                                      nodes[1],
4971                                                      nodes[5 - 2*nextShift],
4972                                                      nodes[5],
4973                                                      nodes[5 + 2*nextShift],
4974                                                      nodes[8] };
4975                 if ( f )
4976                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4977                 else
4978                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4979                                                               newOrder[ 2 ], newOrder[ 3 ],
4980                                                               newOrder[ 4 ], newOrder[ 5 ],
4981                                                               newOrder[ 6 ], newOrder[ 7 ],
4982                                                               newOrder[ 8 ]));
4983               }
4984             }
4985             else  //////// polygon
4986             {
4987               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4988               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4989               if ( !f ||
4990                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4991               {
4992                 if ( !vTool.IsForward() )
4993                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4994                 if ( f )
4995                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4996                 else
4997                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4998               }
4999             }
5000
5001             while ( srcElements.size() < myLastCreatedElems.size() )
5002               srcElements.push_back( *srcEdge );
5003
5004           }  // loop on free faces
5005
5006           // go to the next volume
5007           iVol = 0;
5008           while ( iVol++ < nbVolumesByStep ) v++;
5009
5010         } // loop on steps
5011       } // loop on volumes of one step
5012     } // sweep free links into faces
5013
5014     // Make a ceiling face with a normal external to a volume
5015
5016     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5017     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5018     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5019
5020     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5021       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5022       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5023     }
5024     if ( iF >= 0 )
5025     {
5026       lastVol.SetExternalNormal();
5027       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5028       const               int nbn = lastVol.NbFaceNodes( iF );
5029       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5030       if ( !hasFreeLinks ||
5031            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5032       {
5033         const vector<int>& interlace =
5034           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5035         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5036
5037         AddElement( nodeVec, anyFace.Init( elem ));
5038
5039         while ( srcElements.size() < myLastCreatedElems.size() )
5040           srcElements.push_back( elem );
5041       }
5042     }
5043   } // loop on swept elements
5044 }
5045
5046 //=======================================================================
5047 //function : RotationSweep
5048 //purpose  :
5049 //=======================================================================
5050
5051 SMESH_MeshEditor::PGroupIDs
5052 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5053                                 const gp_Ax1&      theAxis,
5054                                 const double       theAngle,
5055                                 const int          theNbSteps,
5056                                 const double       theTol,
5057                                 const bool         theMakeGroups,
5058                                 const bool         theMakeWalls)
5059 {
5060   ClearLastCreated();
5061
5062   setElemsFirst( theElemSets );
5063   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5064   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5065
5066   // source elements for each generated one
5067   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5068   srcElems.reserve( theElemSets[0].size() );
5069   srcNodes.reserve( theElemSets[1].size() );
5070
5071   gp_Trsf aTrsf;
5072   aTrsf.SetRotation( theAxis, theAngle );
5073   gp_Trsf aTrsf2;
5074   aTrsf2.SetRotation( theAxis, theAngle/2. );
5075
5076   gp_Lin aLine( theAxis );
5077   double aSqTol = theTol * theTol;
5078
5079   SMESHDS_Mesh* aMesh = GetMeshDS();
5080
5081   TNodeOfNodeListMap mapNewNodes;
5082   TElemOfVecOfNnlmiMap mapElemNewNodes;
5083   TTElemOfElemListMap newElemsMap;
5084
5085   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5086                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5087                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5088   // loop on theElemSets
5089   TIDSortedElemSet::iterator itElem;
5090   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5091   {
5092     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5093     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5094       const SMDS_MeshElement* elem = *itElem;
5095       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5096         continue;
5097       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5098       newNodesItVec.reserve( elem->NbNodes() );
5099
5100       // loop on elem nodes
5101       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5102       while ( itN->more() )
5103       {
5104         const SMDS_MeshNode* node = cast2Node( itN->next() );
5105
5106         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5107         double coord[3];
5108         aXYZ.Coord( coord[0], coord[1], coord[2] );
5109         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5110
5111         // check if a node has been already sweeped
5112         TNodeOfNodeListMapItr nIt =
5113           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5114         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5115         if ( listNewNodes.empty() )
5116         {
5117           // check if we are to create medium nodes between corner ones
5118           bool needMediumNodes = false;
5119           if ( isQuadraticMesh )
5120           {
5121             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5122             while (it->more() && !needMediumNodes )
5123             {
5124               const SMDS_MeshElement* invElem = it->next();
5125               if ( invElem != elem && !theElems.count( invElem )) continue;
5126               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5127               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5128                 needMediumNodes = true;
5129             }
5130           }
5131
5132           // make new nodes
5133           const SMDS_MeshNode * newNode = node;
5134           for ( int i = 0; i < theNbSteps; i++ ) {
5135             if ( !isOnAxis ) {
5136               if ( needMediumNodes )  // create a medium node
5137               {
5138                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5139                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5140                 myLastCreatedNodes.push_back(newNode);
5141                 srcNodes.push_back( node );
5142                 listNewNodes.push_back( newNode );
5143                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5144               }
5145               else {
5146                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5147               }
5148               // create a corner node
5149               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5150               myLastCreatedNodes.push_back(newNode);
5151               srcNodes.push_back( node );
5152               listNewNodes.push_back( newNode );
5153             }
5154             else {
5155               listNewNodes.push_back( newNode );
5156               // if ( needMediumNodes )
5157               //   listNewNodes.push_back( newNode );
5158             }
5159           }
5160         }
5161         newNodesItVec.push_back( nIt );
5162       }
5163       // make new elements
5164       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5165     }
5166   }
5167
5168   if ( theMakeWalls )
5169     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5170
5171   PGroupIDs newGroupIDs;
5172   if ( theMakeGroups )
5173     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5174
5175   return newGroupIDs;
5176 }
5177
5178 //=======================================================================
5179 //function : ExtrusParam
5180 //purpose  : standard construction
5181 //=======================================================================
5182
5183 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5184                                             const int                theNbSteps,
5185                                             const std::list<double>& theScales,
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   int nbScales = theScales.size();
5201   if ( nbScales > 0 )
5202   {
5203     if ( IsLinearVariation() && nbScales < theNbSteps )
5204     {
5205       myScales.reserve( theNbSteps );
5206       std::list<double>::const_iterator scale = theScales.begin();
5207       double prevScale = 1.0;
5208       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5209       {
5210         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5211         int    stDelta = Max( 1, iStep - myScales.size());
5212         double scDelta = ( *scale - prevScale ) / stDelta;
5213         for ( int iStep = 0; iStep < stDelta; ++iStep )
5214         {
5215           myScales.push_back( prevScale + scDelta );
5216           prevScale = myScales.back();
5217         }
5218         prevScale = *scale;
5219       }
5220     }
5221     else
5222     {
5223       myScales.assign( theScales.begin(), theScales.end() );
5224     }
5225   }
5226   if ( theBasePoint )
5227   {
5228     myBaseP = *theBasePoint;
5229   }
5230
5231   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5232       ( theTolerance > 0 ))
5233   {
5234     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5235   }
5236   else
5237   {
5238     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5239   }
5240 }
5241
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose  : steps are given explicitly
5245 //=======================================================================
5246
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5248                                             Handle(TColStd_HSequenceOfReal) theSteps,
5249                                             const int                       theFlags,
5250                                             const double                    theTolerance):
5251   myDir( theDir ),
5252   mySteps( theSteps ),
5253   myFlags( theFlags ),
5254   myTolerance( theTolerance ),
5255   myElemsToUse( NULL )
5256 {
5257   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5258       ( theTolerance > 0 ))
5259   {
5260     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5261   }
5262   else
5263   {
5264     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5265   }
5266 }
5267
5268 //=======================================================================
5269 //function : ExtrusParam
5270 //purpose  : for extrusion by normal
5271 //=======================================================================
5272
5273 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5274                                             const int    theNbSteps,
5275                                             const int    theFlags,
5276                                             const int    theDim ):
5277   myDir( 1,0,0 ),
5278   mySteps( new TColStd_HSequenceOfReal ),
5279   myFlags( theFlags ),
5280   myTolerance( 0 ),
5281   myElemsToUse( NULL )
5282 {
5283   for (int i = 0; i < theNbSteps; i++ )
5284     mySteps->Append( theStepSize );
5285
5286   if ( theDim == 1 )
5287   {
5288     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5289   }
5290   else
5291   {
5292     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5293   }
5294 }
5295
5296 //=======================================================================
5297 //function : ExtrusParam::SetElementsToUse
5298 //purpose  : stores elements to use for extrusion by normal, depending on
5299 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5300 //           define myBaseP for scaling
5301 //=======================================================================
5302
5303 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5304                                                       const TIDSortedElemSet& nodes )
5305 {
5306   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5307
5308   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5309   {
5310     myBaseP.SetCoord( 0.,0.,0. );
5311     TIDSortedElemSet newNodes;
5312
5313     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5314     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5315     {
5316       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5317       TIDSortedElemSet::const_iterator itElem = elements.begin();
5318       for ( ; itElem != elements.end(); itElem++ )
5319       {
5320         const SMDS_MeshElement* elem = *itElem;
5321         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5322         while ( itN->more() ) {
5323           const SMDS_MeshElement* node = itN->next();
5324           if ( newNodes.insert( node ).second )
5325             myBaseP += SMESH_NodeXYZ( node );
5326         }
5327       }
5328     }
5329     myBaseP /= newNodes.size();
5330   }
5331 }
5332
5333 //=======================================================================
5334 //function : ExtrusParam::beginStepIter
5335 //purpose  : prepare iteration on steps
5336 //=======================================================================
5337
5338 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5339 {
5340   myWithMediumNodes = withMediumNodes;
5341   myNextStep = 1;
5342   myCurSteps.clear();
5343 }
5344 //=======================================================================
5345 //function : ExtrusParam::moreSteps
5346 //purpose  : are there more steps?
5347 //=======================================================================
5348
5349 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5350 {
5351   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5352 }
5353 //=======================================================================
5354 //function : ExtrusParam::nextStep
5355 //purpose  : returns the next step
5356 //=======================================================================
5357
5358 double SMESH_MeshEditor::ExtrusParam::nextStep()
5359 {
5360   double res = 0;
5361   if ( !myCurSteps.empty() )
5362   {
5363     res = myCurSteps.back();
5364     myCurSteps.pop_back();
5365   }
5366   else if ( myNextStep <= mySteps->Length() )
5367   {
5368     myCurSteps.push_back( mySteps->Value( myNextStep ));
5369     ++myNextStep;
5370     if ( myWithMediumNodes )
5371     {
5372       myCurSteps.back() /= 2.;
5373       myCurSteps.push_back( myCurSteps.back() );
5374     }
5375     res = nextStep();
5376   }
5377   return res;
5378 }
5379
5380 //=======================================================================
5381 //function : ExtrusParam::makeNodesByDir
5382 //purpose  : create nodes for standard extrusion
5383 //=======================================================================
5384
5385 int SMESH_MeshEditor::ExtrusParam::
5386 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5387                 const SMDS_MeshNode*              srcNode,
5388                 std::list<const SMDS_MeshNode*> & newNodes,
5389                 const bool                        makeMediumNodes)
5390 {
5391   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5392
5393   int nbNodes = 0;
5394   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5395   {
5396     p += myDir.XYZ() * nextStep();
5397     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5398     newNodes.push_back( newNode );
5399   }
5400
5401   if ( !myScales.empty() )
5402   {
5403     if ( makeMediumNodes && myMediumScales.empty() )
5404     {
5405       myMediumScales.resize( myScales.size() );
5406       double prevFactor = 1.;
5407       for ( size_t i = 0; i < myScales.size(); ++i )
5408       {
5409         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5410         prevFactor = myScales[i];
5411       }
5412     }
5413     typedef std::vector<double>::iterator ScaleIt;
5414     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5415
5416     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5417
5418     gp_XYZ center = myBaseP;
5419     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5420     size_t iN  = 0;
5421     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5422     {
5423       center += myDir.XYZ() * nextStep();
5424
5425       iSc += int( makeMediumNodes );
5426       ScaleIt& scale = scales[ iSc % 2 ];
5427       
5428       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5429       xyz = ( *scale * ( xyz - center )) + center;
5430       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5431
5432       ++scale;
5433     }
5434   }
5435   return nbNodes;
5436 }
5437
5438 //=======================================================================
5439 //function : ExtrusParam::makeNodesByDirAndSew
5440 //purpose  : create nodes for standard extrusion with sewing
5441 //=======================================================================
5442
5443 int SMESH_MeshEditor::ExtrusParam::
5444 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5445                       const SMDS_MeshNode*              srcNode,
5446                       std::list<const SMDS_MeshNode*> & newNodes,
5447                       const bool                        makeMediumNodes)
5448 {
5449   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5450
5451   int nbNodes = 0;
5452   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5453   {
5454     P1 += myDir.XYZ() * nextStep();
5455
5456     // try to search in sequence of existing nodes
5457     // if myNodes.size()>0 we 'nave to use given sequence
5458     // else - use all nodes of mesh
5459     const SMDS_MeshNode * node = 0;
5460     if ( myNodes.Length() > 0 )
5461     {
5462       for ( int i = 1; i <= myNodes.Length(); i++ )
5463       {
5464         SMESH_NodeXYZ P2 = myNodes.Value(i);
5465         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5466         {
5467           node = myNodes.Value(i);
5468           break;
5469         }
5470       }
5471     }
5472     else
5473     {
5474       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5475       while(itn->more())
5476       {
5477         SMESH_NodeXYZ P2 = itn->next();
5478         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5479         {
5480           node = P2._node;
5481           break;
5482         }
5483       }
5484     }
5485
5486     if ( !node )
5487       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5488
5489     newNodes.push_back( node );
5490
5491   } // loop on steps
5492
5493   return nbNodes;
5494 }
5495
5496 //=======================================================================
5497 //function : ExtrusParam::makeNodesByNormal2D
5498 //purpose  : create nodes for extrusion using normals of faces
5499 //=======================================================================
5500
5501 int SMESH_MeshEditor::ExtrusParam::
5502 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5503                      const SMDS_MeshNode*              srcNode,
5504                      std::list<const SMDS_MeshNode*> & newNodes,
5505                      const bool                        makeMediumNodes)
5506 {
5507   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5508
5509   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5510
5511   // get normals to faces sharing srcNode
5512   vector< gp_XYZ > norms, baryCenters;
5513   gp_XYZ norm, avgNorm( 0,0,0 );
5514   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5515   while ( faceIt->more() )
5516   {
5517     const SMDS_MeshElement* face = faceIt->next();
5518     if ( myElemsToUse && !myElemsToUse->count( face ))
5519       continue;
5520     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5521     {
5522       norms.push_back( norm );
5523       avgNorm += norm;
5524       if ( !alongAvgNorm )
5525       {
5526         gp_XYZ bc(0,0,0);
5527         int nbN = 0;
5528         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5529           bc += SMESH_NodeXYZ( nIt->next() );
5530         baryCenters.push_back( bc / nbN );
5531       }
5532     }
5533   }
5534
5535   if ( norms.empty() ) return 0;
5536
5537   double normSize = avgNorm.Modulus();
5538   if ( normSize < std::numeric_limits<double>::min() )
5539     return 0;
5540
5541   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5542   {
5543     myDir = avgNorm;
5544     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5545   }
5546
5547   avgNorm /= normSize;
5548
5549   int nbNodes = 0;
5550   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5551   {
5552     gp_XYZ pNew = p;
5553     double stepSize = nextStep();
5554
5555     if ( norms.size() > 1 )
5556     {
5557       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5558       {
5559         // translate plane of a face
5560         baryCenters[ iF ] += norms[ iF ] * stepSize;
5561
5562         // find point of intersection of the face plane located at baryCenters[ iF ]
5563         // and avgNorm located at pNew
5564         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5565         double dot  = ( norms[ iF ] * avgNorm );
5566         if ( dot < std::numeric_limits<double>::min() )
5567           dot = stepSize * 1e-3;
5568         double step = -( norms[ iF ] * pNew + d ) / dot;
5569         pNew += step * avgNorm;
5570       }
5571     }
5572     else
5573     {
5574       pNew += stepSize * avgNorm;
5575     }
5576     p = pNew;
5577
5578     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5579     newNodes.push_back( newNode );
5580   }
5581   return nbNodes;
5582 }
5583
5584 //=======================================================================
5585 //function : ExtrusParam::makeNodesByNormal1D
5586 //purpose  : create nodes for extrusion using normals of edges
5587 //=======================================================================
5588
5589 int SMESH_MeshEditor::ExtrusParam::
5590 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5591                      const SMDS_MeshNode*              srcNode,
5592                      std::list<const SMDS_MeshNode*> & newNodes,
5593                      const bool                        makeMediumNodes)
5594 {
5595   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5596   return 0;
5597 }
5598
5599 //=======================================================================
5600 //function : ExtrusionSweep
5601 //purpose  :
5602 //=======================================================================
5603
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5606                                   const gp_Vec&        theStep,
5607                                   const int            theNbSteps,
5608                                   TTElemOfElemListMap& newElemsMap,
5609                                   const int            theFlags,
5610                                   const double         theTolerance)
5611 {
5612   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5613   return ExtrusionSweep( theElems, aParams, newElemsMap );
5614 }
5615
5616
5617 //=======================================================================
5618 //function : ExtrusionSweep
5619 //purpose  :
5620 //=======================================================================
5621
5622 SMESH_MeshEditor::PGroupIDs
5623 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5624                                   ExtrusParam&         theParams,
5625                                   TTElemOfElemListMap& newElemsMap)
5626 {
5627   ClearLastCreated();
5628
5629   setElemsFirst( theElemSets );
5630   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5631   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5632
5633   // source elements for each generated one
5634   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5635   srcElems.reserve( theElemSets[0].size() );
5636   srcNodes.reserve( theElemSets[1].size() );
5637
5638   const int nbSteps = theParams.NbSteps();
5639   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5640
5641   TNodeOfNodeListMap   mapNewNodes;
5642   TElemOfVecOfNnlmiMap mapElemNewNodes;
5643
5644   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5645                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5646                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5647   // loop on theElems
5648   TIDSortedElemSet::iterator itElem;
5649   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5650   {
5651     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5652     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5653     {
5654       // check element type
5655       const SMDS_MeshElement* elem = *itElem;
5656       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5657         continue;
5658
5659       const size_t nbNodes = elem->NbNodes();
5660       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5661       newNodesItVec.reserve( nbNodes );
5662
5663       // loop on elem nodes
5664       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5665       while ( itN->more() )
5666       {
5667         // check if a node has been already sweeped
5668         const SMDS_MeshNode* node = cast2Node( itN->next() );
5669         TNodeOfNodeListMap::iterator nIt =
5670           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5671         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5672         if ( listNewNodes.empty() )
5673         {
5674           // make new nodes
5675
5676           // check if we are to create medium nodes between corner ones
5677           bool needMediumNodes = false;
5678           if ( isQuadraticMesh )
5679           {
5680             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5681             while (it->more() && !needMediumNodes )
5682             {
5683               const SMDS_MeshElement* invElem = it->next();
5684               if ( invElem != elem && !theElems.count( invElem )) continue;
5685               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5686               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5687                 needMediumNodes = true;
5688             }
5689           }
5690           // create nodes for all steps
5691           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5692           {
5693             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5694             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5695             {
5696               myLastCreatedNodes.push_back( *newNodesIt );
5697               srcNodes.push_back( node );
5698             }
5699           }
5700           else
5701           {
5702             break; // newNodesItVec will be shorter than nbNodes
5703           }
5704         }
5705         newNodesItVec.push_back( nIt );
5706       }
5707       // make new elements
5708       if ( newNodesItVec.size() == nbNodes )
5709         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5710     }
5711   }
5712
5713   if ( theParams.ToMakeBoundary() ) {
5714     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5715   }
5716   PGroupIDs newGroupIDs;
5717   if ( theParams.ToMakeGroups() )
5718     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5719
5720   return newGroupIDs;
5721 }
5722
5723 //=======================================================================
5724 //function : ExtrusionAlongTrack
5725 //purpose  :
5726 //=======================================================================
5727 SMESH_MeshEditor::Extrusion_Error
5728 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5729                                        SMESH_subMesh*       theTrack,
5730                                        const SMDS_MeshNode* theN1,
5731                                        const bool           theHasAngles,
5732                                        list<double>&        theAngles,
5733                                        const bool           theLinearVariation,
5734                                        const bool           theHasRefPoint,
5735                                        const gp_Pnt&        theRefPoint,
5736                                        const bool           theMakeGroups)
5737 {
5738   ClearLastCreated();
5739
5740   int aNbE;
5741   std::list<double> aPrms;
5742   TIDSortedElemSet::iterator itElem;
5743
5744   gp_XYZ aGC;
5745   TopoDS_Edge aTrackEdge;
5746   TopoDS_Vertex aV1, aV2;
5747
5748   SMDS_ElemIteratorPtr aItE;
5749   SMDS_NodeIteratorPtr aItN;
5750   SMDSAbs_ElementType aTypeE;
5751
5752   TNodeOfNodeListMap mapNewNodes;
5753
5754   // 1. Check data
5755   aNbE = theElements[0].size() + theElements[1].size();
5756   // nothing to do
5757   if ( !aNbE )
5758     return EXTR_NO_ELEMENTS;
5759
5760   // 1.1 Track Pattern
5761   ASSERT( theTrack );
5762
5763   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5764   if ( !pSubMeshDS )
5765     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5766                                 theHasAngles, theAngles, theLinearVariation,
5767                                 theHasRefPoint, theRefPoint, theMakeGroups );
5768
5769   aItE = pSubMeshDS->GetElements();
5770   while ( aItE->more() ) {
5771     const SMDS_MeshElement* pE = aItE->next();
5772     aTypeE = pE->GetType();
5773     // Pattern must contain links only
5774     if ( aTypeE != SMDSAbs_Edge )
5775       return EXTR_PATH_NOT_EDGE;
5776   }
5777
5778   list<SMESH_MeshEditor_PathPoint> fullList;
5779
5780   const TopoDS_Shape& aS = theTrack->GetSubShape();
5781   // Sub-shape for the Pattern must be an Edge or Wire
5782   if( aS.ShapeType() == TopAbs_EDGE ) {
5783     aTrackEdge = TopoDS::Edge( aS );
5784     // the Edge must not be degenerated
5785     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5786       return EXTR_BAD_PATH_SHAPE;
5787     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5788     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5789     const SMDS_MeshNode* aN1 = aItN->next();
5790     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5791     const SMDS_MeshNode* aN2 = aItN->next();
5792     // starting node must be aN1 or aN2
5793     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5794       return EXTR_BAD_STARTING_NODE;
5795     aItN = pSubMeshDS->GetNodes();
5796     while ( aItN->more() ) {
5797       const SMDS_MeshNode*  pNode = aItN->next();
5798       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5799       double aT = pEPos->GetUParameter();
5800       aPrms.push_back( aT );
5801     }
5802     //Extrusion_Error err =
5803     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5804   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5805     list< SMESH_subMesh* > LSM;
5806     TopTools_SequenceOfShape Edges;
5807     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5808     while(itSM->more()) {
5809       SMESH_subMesh* SM = itSM->next();
5810       LSM.push_back(SM);
5811       const TopoDS_Shape& aS = SM->GetSubShape();
5812       Edges.Append(aS);
5813     }
5814     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5815     int startNid = theN1->GetID();
5816     TColStd_MapOfInteger UsedNums;
5817
5818     int NbEdges = Edges.Length();
5819     int i = 1;
5820     for(; i<=NbEdges; i++) {
5821       int k = 0;
5822       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5823       for(; itLSM!=LSM.end(); itLSM++) {
5824         k++;
5825         if(UsedNums.Contains(k)) continue;
5826         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5827         SMESH_subMesh* locTrack = *itLSM;
5828         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5829         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5830         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5831         const SMDS_MeshNode* aN1 = aItN->next();
5832         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5833         const SMDS_MeshNode* aN2 = aItN->next();
5834         // starting node must be aN1 or aN2
5835         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5836         // 2. Collect parameters on the track edge
5837         aPrms.clear();
5838         aItN = locMeshDS->GetNodes();
5839         while ( aItN->more() ) {
5840           const SMDS_MeshNode* pNode = aItN->next();
5841           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5842           double aT = pEPos->GetUParameter();
5843           aPrms.push_back( aT );
5844         }
5845         list<SMESH_MeshEditor_PathPoint> LPP;
5846         //Extrusion_Error err =
5847         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5848         LLPPs.push_back(LPP);
5849         UsedNums.Add(k);
5850         // update startN for search following edge
5851         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5852         else startNid = aN1->GetID();
5853         break;
5854       }
5855     }
5856     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5857     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5858     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5859     for(; itPP!=firstList.end(); itPP++) {
5860       fullList.push_back( *itPP );
5861     }
5862     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5863     fullList.pop_back();
5864     itLLPP++;
5865     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5866       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5867       itPP = currList.begin();
5868       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5869       gp_Dir D1 = PP1.Tangent();
5870       gp_Dir D2 = PP2.Tangent();
5871       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5872                            (D1.Z()+D2.Z())/2 ) );
5873       PP1.SetTangent(Dnew);
5874       fullList.push_back(PP1);
5875       itPP++;
5876       for(; itPP!=firstList.end(); itPP++) {
5877         fullList.push_back( *itPP );
5878       }
5879       PP1 = fullList.back();
5880       fullList.pop_back();
5881     }
5882     // if wire not closed
5883     fullList.push_back(PP1);
5884     // else ???
5885   }
5886   else {
5887     return EXTR_BAD_PATH_SHAPE;
5888   }
5889
5890   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5891                           theHasRefPoint, theRefPoint, theMakeGroups);
5892 }
5893
5894
5895 //=======================================================================
5896 //function : ExtrusionAlongTrack
5897 //purpose  :
5898 //=======================================================================
5899 SMESH_MeshEditor::Extrusion_Error
5900 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5901                                        SMESH_Mesh*          theTrack,
5902                                        const SMDS_MeshNode* theN1,
5903                                        const bool           theHasAngles,
5904                                        list<double>&        theAngles,
5905                                        const bool           theLinearVariation,
5906                                        const bool           theHasRefPoint,
5907                                        const gp_Pnt&        theRefPoint,
5908                                        const bool           theMakeGroups)
5909 {
5910   ClearLastCreated();
5911
5912   int aNbE;
5913   std::list<double> aPrms;
5914   TIDSortedElemSet::iterator itElem;
5915
5916   gp_XYZ aGC;
5917   TopoDS_Edge aTrackEdge;
5918   TopoDS_Vertex aV1, aV2;
5919
5920   SMDS_ElemIteratorPtr aItE;
5921   SMDS_NodeIteratorPtr aItN;
5922   SMDSAbs_ElementType aTypeE;
5923
5924   TNodeOfNodeListMap mapNewNodes;
5925
5926   // 1. Check data
5927   aNbE = theElements[0].size() + theElements[1].size();
5928   // nothing to do
5929   if ( !aNbE )
5930     return EXTR_NO_ELEMENTS;
5931
5932   // 1.1 Track Pattern
5933   ASSERT( theTrack );
5934
5935   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5936
5937   aItE = pMeshDS->elementsIterator();
5938   while ( aItE->more() ) {
5939     const SMDS_MeshElement* pE = aItE->next();
5940     aTypeE = pE->GetType();
5941     // Pattern must contain links only
5942     if ( aTypeE != SMDSAbs_Edge )
5943       return EXTR_PATH_NOT_EDGE;
5944   }
5945
5946   list<SMESH_MeshEditor_PathPoint> fullList;
5947
5948   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5949
5950   if ( !theTrack->HasShapeToMesh() ) {
5951     //Mesh without shape
5952     const SMDS_MeshNode* currentNode = NULL;
5953     const SMDS_MeshNode* prevNode = theN1;
5954     std::vector<const SMDS_MeshNode*> aNodesList;
5955     aNodesList.push_back(theN1);
5956     int nbEdges = 0, conn=0;
5957     const SMDS_MeshElement* prevElem = NULL;
5958     const SMDS_MeshElement* currentElem = NULL;
5959     int totalNbEdges = theTrack->NbEdges();
5960     SMDS_ElemIteratorPtr nIt;
5961
5962     //check start node
5963     if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5964       return EXTR_BAD_STARTING_NODE;
5965     }
5966
5967     conn = nbEdgeConnectivity(theN1);
5968     if( conn != 1 )
5969       return EXTR_PATH_NOT_EDGE;
5970
5971     aItE = theN1->GetInverseElementIterator();
5972     prevElem = aItE->next();
5973     currentElem = prevElem;
5974     //Get all nodes
5975     if(totalNbEdges == 1 ) {
5976       nIt = currentElem->nodesIterator();
5977       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5978       if(currentNode == prevNode)
5979         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980       aNodesList.push_back(currentNode);
5981     } else {
5982       nIt = currentElem->nodesIterator();
5983       while( nIt->more() ) {
5984         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5985         if(currentNode == prevNode)
5986           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5987         aNodesList.push_back(currentNode);
5988
5989         //case of the closed mesh
5990         if(currentNode == theN1) {
5991           nbEdges++;
5992           break;
5993         }
5994
5995         conn = nbEdgeConnectivity(currentNode);
5996         if(conn > 2) {
5997           return EXTR_PATH_NOT_EDGE;
5998         }else if( conn == 1 && nbEdges > 0 ) {
5999           //End of the path
6000           nbEdges++;
6001           break;
6002         }else {
6003           prevNode = currentNode;
6004           aItE = currentNode->GetInverseElementIterator();
6005           currentElem = aItE->next();
6006           if( currentElem  == prevElem)
6007             currentElem = aItE->next();
6008           nIt = currentElem->nodesIterator();
6009           prevElem = currentElem;
6010           nbEdges++;
6011         }
6012       }
6013     }
6014
6015     if(nbEdges != totalNbEdges)
6016       return EXTR_PATH_NOT_EDGE;
6017
6018     TopTools_SequenceOfShape Edges;
6019     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6020     int startNid = theN1->GetID();
6021     for ( size_t i = 1; i < aNodesList.size(); i++ )
6022     {
6023       gp_Pnt     p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6024       gp_Pnt     p2 = SMESH_NodeXYZ( aNodesList[i] );
6025       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6026       list<SMESH_MeshEditor_PathPoint> LPP;
6027       aPrms.clear();
6028       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6029       LLPPs.push_back(LPP);
6030       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6031       else                                        startNid = aNodesList[i-1]->GetID();
6032     }
6033
6034     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6035     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6036     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6037     for(; itPP!=firstList.end(); itPP++) {
6038       fullList.push_back( *itPP );
6039     }
6040
6041     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6042     SMESH_MeshEditor_PathPoint PP2;
6043     fullList.pop_back();
6044     itLLPP++;
6045     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6046       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6047       itPP = currList.begin();
6048       PP2 = currList.front();
6049       gp_Dir D1 = PP1.Tangent();
6050       gp_Dir D2 = PP2.Tangent();
6051       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6052       PP1.SetTangent(Dnew);
6053       fullList.push_back(PP1);
6054       itPP++;
6055       for(; itPP!=currList.end(); itPP++) {
6056         fullList.push_back( *itPP );
6057       }
6058       PP1 = fullList.back();
6059       fullList.pop_back();
6060     }
6061     fullList.push_back(PP1);
6062
6063   } // Sub-shape for the Pattern must be an Edge or Wire
6064   else if ( aS.ShapeType() == TopAbs_EDGE )
6065   {
6066     aTrackEdge = TopoDS::Edge( aS );
6067     // the Edge must not be degenerated
6068     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6069       return EXTR_BAD_PATH_SHAPE;
6070     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6071     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6072     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6073     // starting node must be aN1 or aN2
6074     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6075       return EXTR_BAD_STARTING_NODE;
6076     aItN = pMeshDS->nodesIterator();
6077     while ( aItN->more() ) {
6078       const SMDS_MeshNode* pNode = aItN->next();
6079       if( pNode==aN1 || pNode==aN2 ) continue;
6080       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6081       double aT = pEPos->GetUParameter();
6082       aPrms.push_back( aT );
6083     }
6084     //Extrusion_Error err =
6085     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6086   }
6087   else if( aS.ShapeType() == TopAbs_WIRE ) {
6088     list< SMESH_subMesh* > LSM;
6089     TopTools_SequenceOfShape Edges;
6090     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6091     for(; eExp.More(); eExp.Next()) {
6092       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6093       if( SMESH_Algo::isDegenerated(E) ) continue;
6094       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6095       if(SM) {
6096         LSM.push_back(SM);
6097         Edges.Append(E);
6098       }
6099     }
6100     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6101     TopoDS_Vertex aVprev;
6102     TColStd_MapOfInteger UsedNums;
6103     int NbEdges = Edges.Length();
6104     int i = 1;
6105     for(; i<=NbEdges; i++) {
6106       int k = 0;
6107       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6108       for(; itLSM!=LSM.end(); itLSM++) {
6109         k++;
6110         if(UsedNums.Contains(k)) continue;
6111         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6112         SMESH_subMesh* locTrack = *itLSM;
6113         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6114         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6115         bool aN1isOK = false, aN2isOK = false;
6116         if ( aVprev.IsNull() ) {
6117           // if previous vertex is not yet defined, it means that we in the beginning of wire
6118           // and we have to find initial vertex corresponding to starting node theN1
6119           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6120           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6121           // starting node must be aN1 or aN2
6122           aN1isOK = ( aN1 && aN1 == theN1 );
6123           aN2isOK = ( aN2 && aN2 == theN1 );
6124         }
6125         else {
6126           // we have specified ending vertex of the previous edge on the previous iteration
6127           // and we have just to check that it corresponds to any vertex in current segment
6128           aN1isOK = aVprev.IsSame( aV1 );
6129           aN2isOK = aVprev.IsSame( aV2 );
6130         }
6131         if ( !aN1isOK && !aN2isOK ) continue;
6132         // 2. Collect parameters on the track edge
6133         aPrms.clear();
6134         aItN = locMeshDS->GetNodes();
6135         while ( aItN->more() ) {
6136           const SMDS_MeshNode*  pNode = aItN->next();
6137           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6138           double aT = pEPos->GetUParameter();
6139           aPrms.push_back( aT );
6140         }
6141         list<SMESH_MeshEditor_PathPoint> LPP;
6142         //Extrusion_Error err =
6143         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6144         LLPPs.push_back(LPP);
6145         UsedNums.Add(k);
6146         // update startN for search following edge
6147         if ( aN1isOK ) aVprev = aV2;
6148         else           aVprev = aV1;
6149         break;
6150       }
6151     }
6152     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6153     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6154     fullList.splice( fullList.end(), firstList );
6155
6156     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6157     fullList.pop_back();
6158     itLLPP++;
6159     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6160       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6161       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6162       gp_Dir D1 = PP1.Tangent();
6163       gp_Dir D2 = PP2.Tangent();
6164       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6165       PP1.SetTangent(Dnew);
6166       fullList.push_back(PP1);
6167       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6168       PP1 = fullList.back();
6169       fullList.pop_back();
6170     }
6171     // if wire not closed
6172     fullList.push_back(PP1);
6173     // else ???
6174   }
6175   else {
6176     return EXTR_BAD_PATH_SHAPE;
6177   }
6178
6179   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6180                           theHasRefPoint, theRefPoint, theMakeGroups);
6181 }
6182
6183
6184 //=======================================================================
6185 //function : makeEdgePathPoints
6186 //purpose  : auxiliary for ExtrusionAlongTrack
6187 //=======================================================================
6188 SMESH_MeshEditor::Extrusion_Error
6189 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6190                                      const TopoDS_Edge&                aTrackEdge,
6191                                      bool                              FirstIsStart,
6192                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6193 {
6194   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6195   aTolVec=1.e-7;
6196   aTolVec2=aTolVec*aTolVec;
6197   double aT1, aT2;
6198   TopoDS_Vertex aV1, aV2;
6199   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6200   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6201   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6202   // 2. Collect parameters on the track edge
6203   aPrms.push_front( aT1 );
6204   aPrms.push_back( aT2 );
6205   // sort parameters
6206   aPrms.sort();
6207   if( FirstIsStart ) {
6208     if ( aT1 > aT2 ) {
6209       aPrms.reverse();
6210     }
6211   }
6212   else {
6213     if ( aT2 > aT1 ) {
6214       aPrms.reverse();
6215     }
6216   }
6217   // 3. Path Points
6218   SMESH_MeshEditor_PathPoint aPP;
6219   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6220   std::list<double>::iterator aItD = aPrms.begin();
6221   for(; aItD != aPrms.end(); ++aItD) {
6222     double aT = *aItD;
6223     gp_Pnt aP3D;
6224     gp_Vec aVec;
6225     aC3D->D1( aT, aP3D, aVec );
6226     aL2 = aVec.SquareMagnitude();
6227     if ( aL2 < aTolVec2 )
6228       return EXTR_CANT_GET_TANGENT;
6229     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6230     aPP.SetPnt( aP3D );
6231     aPP.SetTangent( aTgt );
6232     aPP.SetParameter( aT );
6233     LPP.push_back(aPP);
6234   }
6235   return EXTR_OK;
6236 }
6237
6238
6239 //=======================================================================
6240 //function : makeExtrElements
6241 //purpose  : auxiliary for ExtrusionAlongTrack
6242 //=======================================================================
6243 SMESH_MeshEditor::Extrusion_Error
6244 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6245                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6246                                    const bool                        theHasAngles,
6247                                    list<double>&                     theAngles,
6248                                    const bool                        theLinearVariation,
6249                                    const bool                        theHasRefPoint,
6250                                    const gp_Pnt&                     theRefPoint,
6251                                    const bool                        theMakeGroups)
6252 {
6253   const int aNbTP = fullList.size();
6254
6255   // Angles
6256   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6257     linearAngleVariation(aNbTP-1, theAngles);
6258
6259   // fill vector of path points with angles
6260   vector<SMESH_MeshEditor_PathPoint> aPPs;
6261   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6262   list<double>::iterator                 itAngles = theAngles.begin();
6263   aPPs.push_back( *itPP++ );
6264   for( ; itPP != fullList.end(); itPP++) {
6265     aPPs.push_back( *itPP );
6266     if ( theHasAngles && itAngles != theAngles.end() )
6267       aPPs.back().SetAngle( *itAngles++ );
6268   }
6269
6270   TNodeOfNodeListMap   mapNewNodes;
6271   TElemOfVecOfNnlmiMap mapElemNewNodes;
6272   TTElemOfElemListMap  newElemsMap;
6273   TIDSortedElemSet::iterator itElem;
6274   // source elements for each generated one
6275   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6276
6277   // 3. Center of rotation aV0
6278   gp_Pnt aV0 = theRefPoint;
6279   if ( !theHasRefPoint )
6280   {
6281     gp_XYZ aGC( 0.,0.,0. );
6282     TIDSortedElemSet newNodes;
6283
6284     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6285     {
6286       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6287       itElem = theElements.begin();
6288       for ( ; itElem != theElements.end(); itElem++ )
6289       {
6290         const SMDS_MeshElement* elem = *itElem;
6291         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6292         while ( itN->more() ) {
6293           const SMDS_MeshElement* node = itN->next();
6294           if ( newNodes.insert( node ).second )
6295             aGC += SMESH_NodeXYZ( node );
6296         }
6297       }
6298     }
6299     aGC /= newNodes.size();
6300     aV0.SetXYZ( aGC );
6301   } // if (!theHasRefPoint) {
6302
6303   // 4. Processing the elements
6304   SMESHDS_Mesh* aMesh = GetMeshDS();
6305   list<const SMDS_MeshNode*> emptyList;
6306
6307   setElemsFirst( theElemSets );
6308   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6309   {
6310     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6311     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6312     {
6313       const SMDS_MeshElement* elem = *itElem;
6314
6315       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6316       newNodesItVec.reserve( elem->NbNodes() );
6317
6318       // loop on elem nodes
6319       int nodeIndex = -1;
6320       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6321       while ( itN->more() )
6322       {
6323         ++nodeIndex;
6324         // check if a node has been already processed
6325         const SMDS_MeshNode* node = cast2Node( itN->next() );
6326         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6327         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6328         if ( listNewNodes.empty() )
6329         {
6330           // make new nodes
6331           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6332           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6333           gp_Ax1 anAx1, anAxT1T0;
6334           gp_Dir aDT1x, aDT0x, aDT1T0;
6335
6336           aTolAng=1.e-4;
6337
6338           aV0x = aV0;
6339           aPN0 = SMESH_NodeXYZ( node );
6340
6341           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6342           aP0x = aPP0.Pnt();
6343           aDT0x= aPP0.Tangent();
6344
6345           for ( int j = 1; j < aNbTP; ++j ) {
6346             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6347             aP1x     = aPP1.Pnt();
6348             aDT1x    = aPP1.Tangent();
6349             aAngle1x = aPP1.Angle();
6350
6351             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6352             // Translation
6353             gp_Vec aV01x( aP0x, aP1x );
6354             aTrsf.SetTranslation( aV01x );
6355
6356             // translated point
6357             aV1x = aV0x.Transformed( aTrsf );
6358             aPN1 = aPN0.Transformed( aTrsf );
6359
6360             // rotation 1 [ T1,T0 ]
6361             aAngleT1T0=-aDT1x.Angle( aDT0x );
6362             if (fabs(aAngleT1T0) > aTolAng)
6363             {
6364               aDT1T0=aDT1x^aDT0x;
6365               anAxT1T0.SetLocation( aV1x );
6366               anAxT1T0.SetDirection( aDT1T0 );
6367               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6368
6369               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6370             }
6371
6372             // rotation 2
6373             if ( theHasAngles ) {
6374               anAx1.SetLocation( aV1x );
6375               anAx1.SetDirection( aDT1x );
6376               aTrsfRot.SetRotation( anAx1, aAngle1x );
6377
6378               aPN1 = aPN1.Transformed( aTrsfRot );
6379             }
6380
6381             // make new node
6382             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6383             {
6384               // create additional node
6385               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6386               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6387               myLastCreatedNodes.push_back(newNode);
6388               srcNodes.push_back( node );
6389               listNewNodes.push_back( newNode );
6390             }
6391             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6392             myLastCreatedNodes.push_back(newNode);
6393             srcNodes.push_back( node );
6394             listNewNodes.push_back( newNode );
6395
6396             aPN0 = aPN1;
6397             aP0x = aP1x;
6398             aV0x = aV1x;
6399             aDT0x = aDT1x;
6400           }
6401         }
6402         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6403         {
6404           // if current elem is quadratic and current node is not medium
6405           // we have to check - may be it is needed to insert additional nodes
6406           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6407           if ((int) listNewNodes.size() == aNbTP-1 )
6408           {
6409             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6410             gp_XYZ P(node->X(), node->Y(), node->Z());
6411             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6412             int i;
6413             for(i=0; i<aNbTP-1; i++) {
6414               const SMDS_MeshNode* N = *it;
6415               double x = ( N->X() + P.X() )/2.;
6416               double y = ( N->Y() + P.Y() )/2.;
6417               double z = ( N->Z() + P.Z() )/2.;
6418               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6419               srcNodes.push_back( node );
6420               myLastCreatedNodes.push_back(newN);
6421               aNodes[2*i] = newN;
6422               aNodes[2*i+1] = N;
6423               P = gp_XYZ(N->X(),N->Y(),N->Z());
6424             }
6425             listNewNodes.clear();
6426             for(i=0; i<2*(aNbTP-1); i++) {
6427               listNewNodes.push_back(aNodes[i]);
6428             }
6429           }
6430         }
6431
6432         newNodesItVec.push_back( nIt );
6433       }
6434
6435       // make new elements
6436       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6437     }
6438   }
6439
6440   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6441
6442   if ( theMakeGroups )
6443     generateGroups( srcNodes, srcElems, "extruded");
6444
6445   return EXTR_OK;
6446 }
6447
6448
6449 //=======================================================================
6450 //function : linearAngleVariation
6451 //purpose  : spread values over nbSteps
6452 //=======================================================================
6453
6454 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6455                                             list<double>& Angles)
6456 {
6457   int nbAngles = Angles.size();
6458   if( nbSteps > nbAngles && nbAngles > 0 )
6459   {
6460     vector<double> theAngles(nbAngles);
6461     theAngles.assign( Angles.begin(), Angles.end() );
6462
6463     list<double> res;
6464     double rAn2St = double( nbAngles ) / double( nbSteps );
6465     double angPrev = 0, angle;
6466     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6467     {
6468       double angCur = rAn2St * ( iSt+1 );
6469       double angCurFloor  = floor( angCur );
6470       double angPrevFloor = floor( angPrev );
6471       if ( angPrevFloor == angCurFloor )
6472         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6473       else {
6474         int iP = int( angPrevFloor );
6475         double angPrevCeil = ceil(angPrev);
6476         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6477
6478         int iC = int( angCurFloor );
6479         if ( iC < nbAngles )
6480           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6481
6482         iP = int( angPrevCeil );
6483         while ( iC-- > iP )
6484           angle += theAngles[ iC ];
6485       }
6486       res.push_back(angle);
6487       angPrev = angCur;
6488     }
6489     Angles.swap( res );
6490   }
6491 }
6492
6493
6494 //================================================================================
6495 /*!
6496  * \brief Move or copy theElements applying theTrsf to their nodes
6497  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6498  *  \param theTrsf - transformation to apply
6499  *  \param theCopy - if true, create translated copies of theElems
6500  *  \param theMakeGroups - if true and theCopy, create translated groups
6501  *  \param theTargetMesh - mesh to copy translated elements into
6502  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6503  */
6504 //================================================================================
6505
6506 SMESH_MeshEditor::PGroupIDs
6507 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6508                              const gp_Trsf&     theTrsf,
6509                              const bool         theCopy,
6510                              const bool         theMakeGroups,
6511                              SMESH_Mesh*        theTargetMesh)
6512 {
6513   ClearLastCreated();
6514   myLastCreatedElems.reserve( theElems.size() );
6515
6516   bool needReverse = false;
6517   string groupPostfix;
6518   switch ( theTrsf.Form() ) {
6519   case gp_PntMirror:
6520     needReverse = true;
6521     groupPostfix = "mirrored";
6522     break;
6523   case gp_Ax1Mirror:
6524     groupPostfix = "mirrored";
6525     break;
6526   case gp_Ax2Mirror:
6527     needReverse = true;
6528     groupPostfix = "mirrored";
6529     break;
6530   case gp_Rotation:
6531     groupPostfix = "rotated";
6532     break;
6533   case gp_Translation:
6534     groupPostfix = "translated";
6535     break;
6536   case gp_Scale:
6537     groupPostfix = "scaled";
6538     break;
6539   case gp_CompoundTrsf: // different scale by axis
6540     groupPostfix = "scaled";
6541     break;
6542   default:
6543     needReverse = false;
6544     groupPostfix = "transformed";
6545   }
6546
6547   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6548   SMESHDS_Mesh* aMesh    = GetMeshDS();
6549
6550   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6551   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6552   SMESH_MeshEditor::ElemFeatures elemType;
6553
6554   // map old node to new one
6555   TNodeNodeMap nodeMap;
6556
6557   // elements sharing moved nodes; those of them which have all
6558   // nodes mirrored but are not in theElems are to be reversed
6559   TIDSortedElemSet inverseElemSet;
6560
6561   // source elements for each generated one
6562   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6563
6564   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6565   TIDSortedElemSet orphanNode;
6566
6567   if ( theElems.empty() ) // transform the whole mesh
6568   {
6569     // add all elements
6570     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6571     while ( eIt->more() ) theElems.insert( eIt->next() );
6572     // add orphan nodes
6573     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6574     while ( nIt->more() )
6575     {
6576       const SMDS_MeshNode* node = nIt->next();
6577       if ( node->NbInverseElements() == 0)
6578         orphanNode.insert( node );
6579     }
6580   }
6581
6582   // loop on elements to transform nodes : first orphan nodes then elems
6583   TIDSortedElemSet::iterator itElem;
6584   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6585   for (int i=0; i<2; i++)
6586     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6587     {
6588       const SMDS_MeshElement* elem = *itElem;
6589       if ( !elem )
6590         continue;
6591
6592       // loop on elem nodes
6593       double coord[3];
6594       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6595       while ( itN->more() )
6596       {
6597         const SMDS_MeshNode* node = cast2Node( itN->next() );
6598         // check if a node has been already transformed
6599         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6600           nodeMap.insert( make_pair ( node, node ));
6601         if ( !n2n_isnew.second )
6602           continue;
6603
6604         node->GetXYZ( coord );
6605         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6606         if ( theTargetMesh ) {
6607           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6608           n2n_isnew.first->second = newNode;
6609           myLastCreatedNodes.push_back(newNode);
6610           srcNodes.push_back( node );
6611         }
6612         else if ( theCopy ) {
6613           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6614           n2n_isnew.first->second = newNode;
6615           myLastCreatedNodes.push_back(newNode);
6616           srcNodes.push_back( node );
6617         }
6618         else {
6619           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6620           // node position on shape becomes invalid
6621           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6622             ( SMDS_SpacePosition::originSpacePosition() );
6623         }
6624
6625         // keep inverse elements
6626         if ( !theCopy && !theTargetMesh && needReverse ) {
6627           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6628           while ( invElemIt->more() ) {
6629             const SMDS_MeshElement* iel = invElemIt->next();
6630             inverseElemSet.insert( iel );
6631           }
6632         }
6633       }
6634     } // loop on elems in { &orphanNode, &theElems };
6635
6636   // either create new elements or reverse mirrored ones
6637   if ( !theCopy && !needReverse && !theTargetMesh )
6638     return PGroupIDs();
6639
6640   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6641
6642   // Replicate or reverse elements
6643
6644   std::vector<int> iForw;
6645   vector<const SMDS_MeshNode*> nodes;
6646   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6647   {
6648     const SMDS_MeshElement* elem = *itElem;
6649     if ( !elem ) continue;
6650
6651     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6652     size_t               nbNodes  = elem->NbNodes();
6653     if ( geomType == SMDSGeom_NONE ) continue; // node
6654
6655     nodes.resize( nbNodes );
6656
6657     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6658     {
6659       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6660       if ( !aPolyedre )
6661         continue;
6662       nodes.clear();
6663       bool allTransformed = true;
6664       int nbFaces = aPolyedre->NbFaces();
6665       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6666       {
6667         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6668         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6669         {
6670           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6671           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6672           if ( nodeMapIt == nodeMap.end() )
6673             allTransformed = false; // not all nodes transformed
6674           else
6675             nodes.push_back((*nodeMapIt).second);
6676         }
6677         if ( needReverse && allTransformed )
6678           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6679       }
6680       if ( !allTransformed )
6681         continue; // not all nodes transformed
6682     }
6683     else // ----------------------- the rest element types
6684     {
6685       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6686       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6687       const vector<int>&    i = needReverse ? iRev : iForw;
6688
6689       // find transformed nodes
6690       size_t iNode = 0;
6691       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6692       while ( itN->more() ) {
6693         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6694         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6695         if ( nodeMapIt == nodeMap.end() )
6696           break; // not all nodes transformed
6697         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6698       }
6699       if ( iNode != nbNodes )
6700         continue; // not all nodes transformed
6701     }
6702
6703     if ( editor ) {
6704       // copy in this or a new mesh
6705       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6706         srcElems.push_back( elem );
6707     }
6708     else {
6709       // reverse element as it was reversed by transformation
6710       if ( nbNodes > 2 )
6711         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6712     }
6713
6714   } // loop on elements
6715
6716   if ( editor && editor != this )
6717     myLastCreatedElems.swap( editor->myLastCreatedElems );
6718
6719   PGroupIDs newGroupIDs;
6720
6721   if ( ( theMakeGroups && theCopy ) ||
6722        ( theMakeGroups && theTargetMesh ) )
6723     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6724
6725   return newGroupIDs;
6726 }
6727
6728 //================================================================================
6729 /*!
6730  * \brief Make an offset mesh from a source 2D mesh
6731  *  \param [in] theElements - source faces
6732  *  \param [in] theValue - offset value
6733  *  \param [out] theTgtMesh - a mesh to add offset elements to
6734  *  \param [in] theMakeGroups - to generate groups
6735  *  \return PGroupIDs - IDs of created groups
6736  */
6737 //================================================================================
6738
6739 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6740                                                       const double       theValue,
6741                                                       SMESH_Mesh*        theTgtMesh,
6742                                                       const bool         theMakeGroups,
6743                                                       const bool         theCopyElements,
6744                                                       const bool         theFixSelfIntersection)
6745 {
6746   SMESHDS_Mesh*    meshDS = GetMeshDS();
6747   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748   SMESH_MeshEditor tgtEditor( theTgtMesh );
6749
6750   SMDS_ElemIteratorPtr eIt;
6751   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6753
6754   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6755   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6756   std::unique_ptr< SMDS_Mesh > offsetMesh
6757     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758                                    theFixSelfIntersection,
6759                                    new2OldFaces, new2OldNodes ));
6760   if ( offsetMesh->NbElements() == 0 )
6761     return PGroupIDs(); // MakeOffset() failed
6762
6763
6764   if ( theTgtMesh == myMesh && !theCopyElements )
6765   {
6766     // clear the source elements
6767     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6768     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6769     while ( eIt->more() )
6770       meshDS->RemoveFreeElement( eIt->next(), 0 );
6771   }
6772
6773   // offsetMesh->Modified();
6774   // offsetMesh->CompactMesh(); // make IDs start from 1
6775
6776   // source elements for each generated one
6777   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6778   srcElems.reserve( new2OldFaces.size() );
6779   srcNodes.reserve( new2OldNodes.size() );
6780
6781   ClearLastCreated();
6782   myLastCreatedElems.reserve( new2OldFaces.size() );
6783   myLastCreatedNodes.reserve( new2OldNodes.size() );
6784
6785   // copy offsetMesh to theTgtMesh
6786
6787   int idShift = meshDS->MaxNodeID();
6788   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6789     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6790     {
6791 #ifndef _DEBUG_
6792       if ( n->NbInverseElements() > 0 )
6793 #endif
6794       {
6795         const SMDS_MeshNode* n2 =
6796           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6797         myLastCreatedNodes.push_back( n2 );
6798         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6799       }
6800     }
6801
6802   ElemFeatures elemType;
6803   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6804     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6805     {
6806       elemType.Init( f );
6807       elemType.myNodes.clear();
6808       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6809       {
6810         const SMDS_MeshNode* n2 = nIt->next();
6811         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6812       }
6813       tgtEditor.AddElement( elemType.myNodes, elemType );
6814       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6815     }
6816
6817   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6818
6819   PGroupIDs newGroupIDs;
6820   if ( theMakeGroups )
6821     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6822
6823   return newGroupIDs;
6824 }
6825
6826 //=======================================================================
6827 /*!
6828  * \brief Create groups of elements made during transformation
6829  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6830  *  \param elemGens - elements making corresponding myLastCreatedElems
6831  *  \param postfix - to push_back to names of new groups
6832  *  \param targetMesh - mesh to create groups in
6833  *  \param topPresent - is there are "top" elements that are created by sweeping
6834  */
6835 //=======================================================================
6836
6837 SMESH_MeshEditor::PGroupIDs
6838 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6839                                  const SMESH_SequenceOfElemPtr& elemGens,
6840                                  const std::string&             postfix,
6841                                  SMESH_Mesh*                    targetMesh,
6842                                  const bool                     topPresent)
6843 {
6844   PGroupIDs newGroupIDs( new list<int> );
6845   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6846
6847   // Sort existing groups by types and collect their names
6848
6849   // containers to store an old group and generated new ones;
6850   // 1st new group is for result elems of different type than a source one;
6851   // 2nd new group is for same type result elems ("top" group at extrusion)
6852   using boost::tuple;
6853   using boost::make_tuple;
6854   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6855   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6856   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6857   // group names
6858   set< string > groupNames;
6859
6860   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6861   if ( !groupIt->more() ) return newGroupIDs;
6862
6863   int newGroupID = mesh->GetGroupIds().back()+1;
6864   while ( groupIt->more() )
6865   {
6866     SMESH_Group * group = groupIt->next();
6867     if ( !group ) continue;
6868     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6869     if ( !groupDS || groupDS->IsEmpty() ) continue;
6870     groupNames.insert    ( group->GetName() );
6871     groupDS->SetStoreName( group->GetName() );
6872     const SMDSAbs_ElementType type = groupDS->GetType();
6873     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6874     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6875     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6876     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6877   }
6878
6879   // Loop on nodes and elements to add them in new groups
6880
6881   vector< const SMDS_MeshElement* > resultElems;
6882   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6883   {
6884     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6885     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6886     if ( gens.size() != elems.size() )
6887       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6888
6889     // loop on created elements
6890     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6891     {
6892       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6893       if ( !sourceElem ) {
6894         MESSAGE("generateGroups(): NULL source element");
6895         continue;
6896       }
6897       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6898       if ( groupsOldNew.empty() ) { // no groups of this type at all
6899         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6900           ++iElem; // skip all elements made by sourceElem
6901         continue;
6902       }
6903       // collect all elements made by the iElem-th sourceElem
6904       resultElems.clear();
6905       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6906         if ( resElem != sourceElem )
6907           resultElems.push_back( resElem );
6908       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6909         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6910           if ( resElem != sourceElem )
6911             resultElems.push_back( resElem );
6912
6913       const SMDS_MeshElement* topElem = 0;
6914       if ( isNodes ) // there must be a top element
6915       {
6916         topElem = resultElems.back();
6917         resultElems.pop_back();
6918       }
6919       else
6920       {
6921         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6922         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6923           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6924           {
6925             topElem = *resElemIt;
6926             *resElemIt = 0; // erase *resElemIt
6927             break;
6928           }
6929       }
6930       // add resultElems to groups originted from ones the sourceElem belongs to
6931       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6932       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6933       {
6934         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6935         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6936         {
6937           // fill in a new group
6938           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6939           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6940           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6941             if ( *resElemIt )
6942               newGroup.Add( *resElemIt );
6943
6944           // fill a "top" group
6945           if ( topElem )
6946           {
6947             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6948             newTopGroup.Add( topElem );
6949           }
6950         }
6951       }
6952     } // loop on created elements
6953   }// loop on nodes and elements
6954
6955   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6956
6957   list<int> topGrouIds;
6958   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6959   {
6960     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6961     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6962                                       orderedOldNewGroups[i]->get<2>() };
6963     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6964     {
6965       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6966       if ( newGroupDS->IsEmpty() )
6967       {
6968         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6969       }
6970       else
6971       {
6972         // set group type
6973         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6974
6975         // make a name
6976         const bool isTop = ( topPresent &&
6977                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6978                              is2nd );
6979
6980         string name = oldGroupDS->GetStoreName();
6981         { // remove trailing whitespaces (issue 22599)
6982           size_t size = name.size();
6983           while ( size > 1 && isspace( name[ size-1 ]))
6984             --size;
6985           if ( size != name.size() )
6986           {
6987             name.resize( size );
6988             oldGroupDS->SetStoreName( name.c_str() );
6989           }
6990         }
6991         if ( !targetMesh ) {
6992           string suffix = ( isTop ? "top": postfix.c_str() );
6993           name += "_";
6994           name += suffix;
6995           int nb = 1;
6996           while ( !groupNames.insert( name ).second ) // name exists
6997             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6998         }
6999         else if ( isTop ) {
7000           name += "_top";
7001         }
7002         newGroupDS->SetStoreName( name.c_str() );
7003
7004         // make a SMESH_Groups
7005         mesh->AddGroup( newGroupDS );
7006         if ( isTop )
7007           topGrouIds.push_back( newGroupDS->GetID() );
7008         else
7009           newGroupIDs->push_back( newGroupDS->GetID() );
7010       }
7011     }
7012   }
7013   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7014
7015   return newGroupIDs;
7016 }
7017
7018 //================================================================================
7019 /*!
7020  *  * \brief Return list of group of nodes close to each other within theTolerance
7021  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7022  *  *        an Octree algorithm
7023  *  \param [in,out] theNodes - the nodes to treat
7024  *  \param [in]     theTolerance - the tolerance
7025  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7026  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7027  *         corner and medium nodes in separate groups
7028  */
7029 //================================================================================
7030
7031 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7032                                             const double         theTolerance,
7033                                             TListOfListOfNodes & theGroupsOfNodes,
7034                                             bool                 theSeparateCornersAndMedium)
7035 {
7036   ClearLastCreated();
7037
7038   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7039        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7040        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7041     theSeparateCornersAndMedium = false;
7042
7043   TIDSortedNodeSet& corners = theNodes;
7044   TIDSortedNodeSet  medium;
7045
7046   if ( theNodes.empty() ) // get all nodes in the mesh
7047   {
7048     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7049     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7050     if ( theSeparateCornersAndMedium )
7051       while ( nIt->more() )
7052       {
7053         const SMDS_MeshNode* n = nIt->next();
7054         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7055         nodeSet->insert( nodeSet->end(), n );
7056       }
7057     else
7058       while ( nIt->more() )
7059         theNodes.insert( theNodes.end(), nIt->next() );
7060   }
7061   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7062   {
7063     TIDSortedNodeSet::iterator nIt = corners.begin();
7064     while ( nIt != corners.end() )
7065       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7066       {
7067         medium.insert( medium.end(), *nIt );
7068         corners.erase( nIt++ );
7069       }
7070       else
7071       {
7072         ++nIt;
7073       }
7074   }
7075
7076   if ( !corners.empty() )
7077     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7078   if ( !medium.empty() )
7079     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7080 }
7081
7082 //=======================================================================
7083 //function : SimplifyFace
7084 //purpose  : split a chain of nodes into several closed chains
7085 //=======================================================================
7086
7087 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7088                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7089                                     vector<int>&                         quantities) const
7090 {
7091   int nbNodes = faceNodes.size();
7092   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7093     --nbNodes;
7094   if ( nbNodes < 3 )
7095     return 0;
7096   size_t prevNbQuant = quantities.size();
7097
7098   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7099   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7100   map< const SMDS_MeshNode*, int >::iterator nInd;
7101
7102   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7103   simpleNodes.push_back( faceNodes[0] );
7104   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7105   {
7106     if ( faceNodes[ iCur ] != simpleNodes.back() )
7107     {
7108       int index = simpleNodes.size();
7109       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7110       int prevIndex = nInd->second;
7111       if ( prevIndex < index )
7112       {
7113         // a sub-loop found
7114         int loopLen = index - prevIndex;
7115         if ( loopLen > 2 )
7116         {
7117           // store the sub-loop
7118           quantities.push_back( loopLen );
7119           for ( int i = prevIndex; i < index; i++ )
7120             poly_nodes.push_back( simpleNodes[ i ]);
7121         }
7122         simpleNodes.resize( prevIndex+1 );
7123       }
7124       else
7125       {
7126         simpleNodes.push_back( faceNodes[ iCur ]);
7127       }
7128     }
7129   }
7130
7131   if ( simpleNodes.size() > 2 )
7132   {
7133     quantities.push_back( simpleNodes.size() );
7134     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7135   }
7136
7137   return quantities.size() - prevNbQuant;
7138 }
7139
7140 //=======================================================================
7141 //function : MergeNodes
7142 //purpose  : In each group, the cdr of nodes are substituted by the first one
7143 //           in all elements.
7144 //=======================================================================
7145
7146 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7147                                    const bool           theAvoidMakingHoles)
7148 {
7149   ClearLastCreated();
7150
7151   SMESHDS_Mesh* mesh = GetMeshDS();
7152
7153   TNodeNodeMap nodeNodeMap; // node to replace - new node
7154   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7155   list< int > rmElemIds, rmNodeIds;
7156   vector< ElemFeatures > newElemDefs;
7157
7158   // Fill nodeNodeMap and elems
7159
7160   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7161   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7162   {
7163     list<const SMDS_MeshNode*>& nodes = *grIt;
7164     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7165     const SMDS_MeshNode* nToKeep = *nIt;
7166     for ( ++nIt; nIt != nodes.end(); nIt++ )
7167     {
7168       const SMDS_MeshNode* nToRemove = *nIt;
7169       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7170       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7171       while ( invElemIt->more() ) {
7172         const SMDS_MeshElement* elem = invElemIt->next();
7173         elems.insert(elem);
7174       }
7175     }
7176   }
7177
7178   // Apply recursive replacements (BUG 0020185)
7179   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7180   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7181   {
7182     const SMDS_MeshNode* nToKeep = nnIt->second;
7183     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7184     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7185     {
7186       nToKeep = nnIt_i->second;
7187       nnIt->second = nToKeep;
7188       nnIt_i = nodeNodeMap.find( nToKeep );
7189     }
7190   }
7191
7192   if ( theAvoidMakingHoles )
7193   {
7194     // find elements whose topology changes
7195
7196     vector<const SMDS_MeshElement*> pbElems;
7197     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7198     for ( ; eIt != elems.end(); ++eIt )
7199     {
7200       const SMDS_MeshElement* elem = *eIt;
7201       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7202       while ( itN->more() )
7203       {
7204         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7205         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7206         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7207         {
7208           // several nodes of elem stick
7209           pbElems.push_back( elem );
7210           break;
7211         }
7212       }
7213     }
7214     // exclude from merge nodes causing spoiling element
7215     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7216     {
7217       bool nodesExcluded = false;
7218       for ( size_t i = 0; i < pbElems.size(); ++i )
7219       {
7220         size_t prevNbMergeNodes = nodeNodeMap.size();
7221         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7222              prevNbMergeNodes < nodeNodeMap.size() )
7223           nodesExcluded = true;
7224       }
7225       if ( !nodesExcluded )
7226         break;
7227     }
7228   }
7229
7230   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7231   {
7232     const SMDS_MeshNode* nToRemove = nnIt->first;
7233     const SMDS_MeshNode* nToKeep   = nnIt->second;
7234     if ( nToRemove != nToKeep )
7235     {
7236       rmNodeIds.push_back( nToRemove->GetID() );
7237       AddToSameGroups( nToKeep, nToRemove, mesh );
7238       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7239       // w/o creating node in place of merged ones.
7240       SMDS_PositionPtr pos = nToRemove->GetPosition();
7241       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7242         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7243           sm->SetIsAlwaysComputed( true );
7244     }
7245   }
7246
7247   // Change element nodes or remove an element
7248
7249   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7250   for ( ; eIt != elems.end(); eIt++ )
7251   {
7252     const SMDS_MeshElement* elem = *eIt;
7253     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7254
7255     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7256     if ( !keepElem )
7257       rmElemIds.push_back( elem->GetID() );
7258
7259     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7260     {
7261       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7262                                                & newElemDefs[i].myNodes[0],
7263                                                newElemDefs[i].myNodes.size() ))
7264       {
7265         if ( i == 0 )
7266         {
7267           newElemDefs[i].SetID( elem->GetID() );
7268           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7269           if ( !keepElem ) rmElemIds.pop_back();
7270         }
7271         else
7272         {
7273           newElemDefs[i].SetID( -1 );
7274         }
7275         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7276         if ( sm && newElem )
7277           sm->AddElement( newElem );
7278         if ( elem != newElem )
7279           ReplaceElemInGroups( elem, newElem, mesh );
7280       }
7281     }
7282   }
7283
7284   // Remove bad elements, then equal nodes (order important)
7285   Remove( rmElemIds, /*isNodes=*/false );
7286   Remove( rmNodeIds, /*isNodes=*/true );
7287
7288   return;
7289 }
7290
7291 //=======================================================================
7292 //function : applyMerge
7293 //purpose  : Compute new connectivity of an element after merging nodes
7294 //  \param [in] elems - the element
7295 //  \param [out] newElemDefs - definition(s) of result element(s)
7296 //  \param [inout] nodeNodeMap - nodes to merge
7297 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7298 //              after merging (but not degenerated), removes nodes causing
7299 //              the invalidity from \a nodeNodeMap.
7300 //  \return bool - true if the element should be removed
7301 //=======================================================================
7302
7303 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7304                                    vector< ElemFeatures >& newElemDefs,
7305                                    TNodeNodeMap&           nodeNodeMap,
7306                                    const bool              avoidMakingHoles )
7307 {
7308   bool toRemove = false; // to remove elem
7309   int nbResElems = 1;    // nb new elements
7310
7311   newElemDefs.resize(nbResElems);
7312   newElemDefs[0].Init( elem );
7313   newElemDefs[0].myNodes.clear();
7314
7315   set<const SMDS_MeshNode*> nodeSet;
7316   vector< const SMDS_MeshNode*>   curNodes;
7317   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7318   vector<int> iRepl;
7319
7320   const        int  nbNodes = elem->NbNodes();
7321   SMDSAbs_EntityType entity = elem->GetEntityType();
7322
7323   curNodes.resize( nbNodes );
7324   uniqueNodes.resize( nbNodes );
7325   iRepl.resize( nbNodes );
7326   int iUnique = 0, iCur = 0, nbRepl = 0;
7327
7328   // Get new seq of nodes
7329
7330   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7331   while ( itN->more() )
7332   {
7333     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7334
7335     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7336     if ( nnIt != nodeNodeMap.end() ) {
7337       n = (*nnIt).second;
7338     }
7339     curNodes[ iCur ] = n;
7340     bool isUnique = nodeSet.insert( n ).second;
7341     if ( isUnique )
7342       uniqueNodes[ iUnique++ ] = n;
7343     else
7344       iRepl[ nbRepl++ ] = iCur;
7345     iCur++;
7346   }
7347
7348   // Analyse element topology after replacement
7349
7350   int nbUniqueNodes = nodeSet.size();
7351   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7352   {
7353     toRemove = true;
7354     nbResElems = 0;
7355
7356     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7357     {
7358       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7359       int nbCorners = nbNodes / 2;
7360       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7361       {
7362         int iNext = ( iCur + 1 ) % nbCorners;
7363         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7364         {
7365           int iMedium = iCur + nbCorners;
7366           vector< const SMDS_MeshNode* >::iterator i =
7367             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7368                        uniqueNodes.end(),
7369                        curNodes[ iMedium ]);
7370           if ( i != uniqueNodes.end() )
7371           {
7372             --nbUniqueNodes;
7373             for ( ; i+1 != uniqueNodes.end(); ++i )
7374               *i = *(i+1);
7375           }
7376         }
7377       }
7378     }
7379
7380     switch ( entity )
7381     {
7382     case SMDSEntity_Polygon:
7383     case SMDSEntity_Quad_Polygon: // Polygon
7384     {
7385       ElemFeatures* elemType = & newElemDefs[0];
7386       const bool isQuad = elemType->myIsQuad;
7387       if ( isQuad )
7388         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7389           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7390
7391       // a polygon can divide into several elements
7392       vector<const SMDS_MeshNode *> polygons_nodes;
7393       vector<int> quantities;
7394       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7395       newElemDefs.resize( nbResElems );
7396       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7397       {
7398         ElemFeatures* elemType = & newElemDefs[iface];
7399         if ( iface ) elemType->Init( elem );
7400
7401         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7402         int nbNewNodes = quantities[iface];
7403         face_nodes.assign( polygons_nodes.begin() + inode,
7404                            polygons_nodes.begin() + inode + nbNewNodes );
7405         inode += nbNewNodes;
7406         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7407         {
7408           bool isValid = ( nbNewNodes % 2 == 0 );
7409           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7410             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7411           elemType->SetQuad( isValid );
7412           if ( isValid ) // put medium nodes after corners
7413             SMDS_MeshCell::applyInterlaceRev
7414               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7415                                                     nbNewNodes ), face_nodes );
7416         }
7417         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7418       }
7419       nbUniqueNodes = newElemDefs[0].myNodes.size();
7420       break;
7421     } // Polygon
7422
7423     case SMDSEntity_Polyhedra: // Polyhedral volume
7424     {
7425       if ( nbUniqueNodes >= 4 )
7426       {
7427         // each face has to be analyzed in order to check volume validity
7428         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7429         {
7430           int nbFaces = aPolyedre->NbFaces();
7431
7432           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7433           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7434           vector<const SMDS_MeshNode *>  faceNodes;
7435           poly_nodes.clear();
7436           quantities.clear();
7437
7438           for (int iface = 1; iface <= nbFaces; iface++)
7439           {
7440             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7441             faceNodes.resize( nbFaceNodes );
7442             for (int inode = 1; inode <= nbFaceNodes; inode++)
7443             {
7444               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7445               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7446               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7447                 faceNode = (*nnIt).second;
7448               faceNodes[inode - 1] = faceNode;
7449             }
7450             SimplifyFace(faceNodes, poly_nodes, quantities);
7451           }
7452
7453           if ( quantities.size() > 3 )
7454           {
7455             // TODO: remove coincident faces
7456             nbResElems = 1;
7457             nbUniqueNodes = newElemDefs[0].myNodes.size();
7458           }
7459         }
7460       }
7461     }
7462     break;
7463
7464     // Regular elements
7465     // TODO not all the possible cases are solved. Find something more generic?
7466     case SMDSEntity_Edge: //////// EDGE
7467     case SMDSEntity_Triangle: //// TRIANGLE
7468     case SMDSEntity_Quad_Triangle:
7469     case SMDSEntity_Tetra:
7470     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7471     {
7472       break;
7473     }
7474     case SMDSEntity_Quad_Edge:
7475     {
7476       break;
7477     }
7478     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7479     {
7480       if ( nbUniqueNodes < 3 )
7481         toRemove = true;
7482       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7483         toRemove = true; // opposite nodes stick
7484       else
7485         toRemove = false;
7486       break;
7487     }
7488     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7489     {
7490       //   1    5    2
7491       //    +---+---+
7492       //    |       |
7493       //   4+       +6
7494       //    |       |
7495       //    +---+---+
7496       //   0    7    3
7497       if ( nbUniqueNodes == 6 &&
7498            iRepl[0] < 4       &&
7499            ( nbRepl == 1 || iRepl[1] >= 4 ))
7500       {
7501         toRemove = false;
7502       }
7503       break;
7504     }
7505     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7506     {
7507       //   1    5    2
7508       //    +---+---+
7509       //    |       |
7510       //   4+  8+   +6
7511       //    |       |
7512       //    +---+---+
7513       //   0    7    3
7514       if ( nbUniqueNodes == 7 &&
7515            iRepl[0] < 4       &&
7516            ( nbRepl == 1 || iRepl[1] != 8 ))
7517       {
7518         toRemove = false;
7519       }
7520       break;
7521     }
7522     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7523     {
7524       if ( nbUniqueNodes == 4 ) {
7525         // ---------------------------------> tetrahedron
7526         if ( curNodes[3] == curNodes[4] &&
7527              curNodes[3] == curNodes[5] ) {
7528           // top nodes stick
7529           toRemove = false;
7530         }
7531         else if ( curNodes[0] == curNodes[1] &&
7532                   curNodes[0] == curNodes[2] ) {
7533           // bottom nodes stick: set a top before
7534           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7535           uniqueNodes[ 0 ] = curNodes [ 5 ];
7536           uniqueNodes[ 1 ] = curNodes [ 4 ];
7537           uniqueNodes[ 2 ] = curNodes [ 3 ];
7538           toRemove = false;
7539         }
7540         else if (( curNodes[0] == curNodes[3] ) +
7541                  ( curNodes[1] == curNodes[4] ) +
7542                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7543           // a lateral face turns into a line
7544           toRemove = false;
7545         }
7546       }
7547       else if ( nbUniqueNodes == 5 ) {
7548         // PENTAHEDRON --------------------> pyramid
7549         if ( curNodes[0] == curNodes[3] )
7550         {
7551           uniqueNodes[ 0 ] = curNodes[ 1 ];
7552           uniqueNodes[ 1 ] = curNodes[ 4 ];
7553           uniqueNodes[ 2 ] = curNodes[ 5 ];
7554           uniqueNodes[ 3 ] = curNodes[ 2 ];
7555           uniqueNodes[ 4 ] = curNodes[ 0 ];
7556           toRemove = false;
7557         }
7558         if ( curNodes[1] == curNodes[4] )
7559         {
7560           uniqueNodes[ 0 ] = curNodes[ 0 ];
7561           uniqueNodes[ 1 ] = curNodes[ 2 ];
7562           uniqueNodes[ 2 ] = curNodes[ 5 ];
7563           uniqueNodes[ 3 ] = curNodes[ 3 ];
7564           uniqueNodes[ 4 ] = curNodes[ 1 ];
7565           toRemove = false;
7566         }
7567         if ( curNodes[2] == curNodes[5] )
7568         {
7569           uniqueNodes[ 0 ] = curNodes[ 0 ];
7570           uniqueNodes[ 1 ] = curNodes[ 3 ];
7571           uniqueNodes[ 2 ] = curNodes[ 4 ];
7572           uniqueNodes[ 3 ] = curNodes[ 1 ];
7573           uniqueNodes[ 4 ] = curNodes[ 2 ];
7574           toRemove = false;
7575         }
7576       }
7577       break;
7578     }
7579     case SMDSEntity_Hexa:
7580     {
7581       //////////////////////////////////// HEXAHEDRON
7582       SMDS_VolumeTool hexa (elem);
7583       hexa.SetExternalNormal();
7584       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7585         //////////////////////// HEX ---> tetrahedron
7586         for ( int iFace = 0; iFace < 6; iFace++ ) {
7587           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7588           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7589               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7590               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7591             // one face turns into a point ...
7592             int  pickInd = ind[ 0 ];
7593             int iOppFace = hexa.GetOppFaceIndex( iFace );
7594             ind = hexa.GetFaceNodesIndices( iOppFace );
7595             int nbStick = 0;
7596             uniqueNodes.clear();
7597             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7598               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7599                 nbStick++;
7600               else
7601                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7602             }
7603             if ( nbStick == 1 ) {
7604               // ... and the opposite one - into a triangle.
7605               // set a top node
7606               uniqueNodes.push_back( curNodes[ pickInd ]);
7607               toRemove = false;
7608             }
7609             break;
7610           }
7611         }
7612       }
7613       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7614         //////////////////////// HEX ---> prism
7615         int nbTria = 0, iTria[3];
7616         const int *ind; // indices of face nodes
7617         // look for triangular faces
7618         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7619           ind = hexa.GetFaceNodesIndices( iFace );
7620           TIDSortedNodeSet faceNodes;
7621           for ( iCur = 0; iCur < 4; iCur++ )
7622             faceNodes.insert( curNodes[ind[iCur]] );
7623           if ( faceNodes.size() == 3 )
7624             iTria[ nbTria++ ] = iFace;
7625         }
7626         // check if triangles are opposite
7627         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7628         {
7629           // set nodes of the bottom triangle
7630           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7631           vector<int> indB;
7632           for ( iCur = 0; iCur < 4; iCur++ )
7633             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7634               indB.push_back( ind[iCur] );
7635           if ( !hexa.IsForward() )
7636             std::swap( indB[0], indB[2] );
7637           for ( iCur = 0; iCur < 3; iCur++ )
7638             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7639           // set nodes of the top triangle
7640           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7641           for ( iCur = 0; iCur < 3; ++iCur )
7642             for ( int j = 0; j < 4; ++j )
7643               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7644               {
7645                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7646                 break;
7647               }
7648           toRemove = false;
7649           break;
7650         }
7651       }
7652       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7653         //////////////////// HEXAHEDRON ---> pyramid
7654         for ( int iFace = 0; iFace < 6; iFace++ ) {
7655           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7656           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7657               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7658               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7659             // one face turns into a point ...
7660             int iOppFace = hexa.GetOppFaceIndex( iFace );
7661             ind = hexa.GetFaceNodesIndices( iOppFace );
7662             uniqueNodes.clear();
7663             for ( iCur = 0; iCur < 4; iCur++ ) {
7664               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7665                 break;
7666               else
7667                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7668             }
7669             if ( uniqueNodes.size() == 4 ) {
7670               // ... and the opposite one is a quadrangle
7671               // set a top node
7672               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7673               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7674               toRemove = false;
7675             }
7676             break;
7677           }
7678         }
7679       }
7680
7681       if ( toRemove && nbUniqueNodes > 4 ) {
7682         ////////////////// HEXAHEDRON ---> polyhedron
7683         hexa.SetExternalNormal();
7684         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7685         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7686         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7687         quantities.reserve( 6 );     quantities.clear();
7688         for ( int iFace = 0; iFace < 6; iFace++ )
7689         {
7690           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7691           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7692                curNodes[ind[1]] == curNodes[ind[3]] )
7693           {
7694             quantities.clear();
7695             break; // opposite nodes stick
7696           }
7697           nodeSet.clear();
7698           for ( iCur = 0; iCur < 4; iCur++ )
7699           {
7700             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7701               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7702           }
7703           if ( nodeSet.size() < 3 )
7704             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7705           else
7706             quantities.push_back( nodeSet.size() );
7707         }
7708         if ( quantities.size() >= 4 )
7709         {
7710           nbResElems = 1;
7711           nbUniqueNodes = poly_nodes.size();
7712           newElemDefs[0].SetPoly(true);
7713         }
7714       }
7715       break;
7716     } // case HEXAHEDRON
7717
7718     default:
7719       toRemove = true;
7720
7721     } // switch ( entity )
7722
7723     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7724     {
7725       // erase from nodeNodeMap nodes whose merge spoils elem
7726       vector< const SMDS_MeshNode* > noMergeNodes;
7727       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7728       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7729         nodeNodeMap.erase( noMergeNodes[i] );
7730     }
7731     
7732   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7733
7734   uniqueNodes.resize( nbUniqueNodes );
7735
7736   if ( !toRemove && nbResElems == 0 )
7737     nbResElems = 1;
7738
7739   newElemDefs.resize( nbResElems );
7740
7741   return !toRemove;
7742 }
7743
7744
7745 // ========================================================
7746 // class   : ComparableElement
7747 // purpose : allow comparing elements basing on their nodes
7748 // ========================================================
7749
7750 class ComparableElement : public boost::container::flat_set< int >
7751 {
7752   typedef boost::container::flat_set< int >  int_set;
7753
7754   const SMDS_MeshElement* myElem;
7755   int                     mySumID;
7756   mutable int             myGroupID;
7757
7758 public:
7759
7760   ComparableElement( const SMDS_MeshElement* theElem ):
7761     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7762   {
7763     this->reserve( theElem->NbNodes() );
7764     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7765     {
7766       int id = nodeIt->next()->GetID();
7767       mySumID += id;
7768       this->insert( id );
7769     }
7770   }
7771
7772   const SMDS_MeshElement* GetElem() const { return myElem; }
7773
7774   int& GroupID() const { return myGroupID; }
7775   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7776
7777   ComparableElement( const ComparableElement& theSource ) // move copy
7778   {
7779     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7780     (int_set&) (*this ) = boost::move( src );
7781     myElem    = src.myElem;
7782     mySumID   = src.mySumID;
7783     myGroupID = src.myGroupID;
7784   }
7785
7786   static int HashCode(const ComparableElement& se, int limit )
7787   {
7788     return ::HashCode( se.mySumID, limit );
7789   }
7790   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7791   {
7792     return ( se1 == se2 );
7793   }
7794
7795 };
7796
7797 //=======================================================================
7798 //function : FindEqualElements
7799 //purpose  : Return list of group of elements built on the same nodes.
7800 //           Search among theElements or in the whole mesh if theElements is empty
7801 //=======================================================================
7802
7803 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7804                                           TListOfListOfElementsID & theGroupsOfElementsID )
7805 {
7806   ClearLastCreated();
7807
7808   SMDS_ElemIteratorPtr elemIt;
7809   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7810   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7811
7812   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7813   typedef std::list<int>                                          TGroupOfElems;
7814   TMapOfElements               mapOfElements;
7815   std::vector< TGroupOfElems > arrayOfGroups;
7816   TGroupOfElems                groupOfElems;
7817
7818   while ( elemIt->more() )
7819   {
7820     const SMDS_MeshElement* curElem = elemIt->next();
7821     ComparableElement      compElem = curElem;
7822     // check uniqueness
7823     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7824     if ( elemInSet.GetElem() != curElem ) // coincident elem
7825     {
7826       int& iG = elemInSet.GroupID();
7827       if ( iG < 0 )
7828       {
7829         iG = arrayOfGroups.size();
7830         arrayOfGroups.push_back( groupOfElems );
7831         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7832       }
7833       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7834     }
7835   }
7836
7837   groupOfElems.clear();
7838   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7839   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7840   {
7841     if ( groupIt->size() > 1 ) {
7842       //groupOfElems.sort(); -- theElements are sorted already
7843       theGroupsOfElementsID.emplace_back( *groupIt );
7844     }
7845   }
7846 }
7847
7848 //=======================================================================
7849 //function : MergeElements
7850 //purpose  : In each given group, substitute all elements by the first one.
7851 //=======================================================================
7852
7853 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7854 {
7855   ClearLastCreated();
7856
7857   typedef list<int> TListOfIDs;
7858   TListOfIDs rmElemIds; // IDs of elems to remove
7859
7860   SMESHDS_Mesh* aMesh = GetMeshDS();
7861
7862   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7863   while ( groupsIt != theGroupsOfElementsID.end() ) {
7864     TListOfIDs& aGroupOfElemID = *groupsIt;
7865     aGroupOfElemID.sort();
7866     int elemIDToKeep = aGroupOfElemID.front();
7867     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7868     aGroupOfElemID.pop_front();
7869     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7870     while ( idIt != aGroupOfElemID.end() ) {
7871       int elemIDToRemove = *idIt;
7872       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7873       // add the kept element in groups of removed one (PAL15188)
7874       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7875       rmElemIds.push_back( elemIDToRemove );
7876       ++idIt;
7877     }
7878     ++groupsIt;
7879   }
7880
7881   Remove( rmElemIds, false );
7882 }
7883
7884 //=======================================================================
7885 //function : MergeEqualElements
7886 //purpose  : Remove all but one of elements built on the same nodes.
7887 //=======================================================================
7888
7889 void SMESH_MeshEditor::MergeEqualElements()
7890 {
7891   TIDSortedElemSet aMeshElements; /* empty input ==
7892                                      to merge equal elements in the whole mesh */
7893   TListOfListOfElementsID aGroupsOfElementsID;
7894   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7895   MergeElements( aGroupsOfElementsID );
7896 }
7897
7898 //=======================================================================
7899 //function : findAdjacentFace
7900 //purpose  :
7901 //=======================================================================
7902
7903 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7904                                                 const SMDS_MeshNode* n2,
7905                                                 const SMDS_MeshElement* elem)
7906 {
7907   TIDSortedElemSet elemSet, avoidSet;
7908   if ( elem )
7909     avoidSet.insert ( elem );
7910   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7911 }
7912
7913 //=======================================================================
7914 //function : findSegment
7915 //purpose  : Return a mesh segment by two nodes one of which can be medium
7916 //=======================================================================
7917
7918 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7919                                            const SMDS_MeshNode* n2)
7920 {
7921   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7922   while ( it->more() )
7923   {
7924     const SMDS_MeshElement* seg = it->next();
7925     if ( seg->GetNodeIndex( n2 ) >= 0 )
7926       return seg;
7927   }
7928   return 0;
7929 }
7930
7931 //=======================================================================
7932 //function : FindFreeBorder
7933 //purpose  :
7934 //=======================================================================
7935
7936 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7937
7938 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7939                                        const SMDS_MeshNode*             theSecondNode,
7940                                        const SMDS_MeshNode*             theLastNode,
7941                                        list< const SMDS_MeshNode* > &   theNodes,
7942                                        list< const SMDS_MeshElement* >& theFaces)
7943 {
7944   if ( !theFirstNode || !theSecondNode )
7945     return false;
7946   // find border face between theFirstNode and theSecondNode
7947   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7948   if ( !curElem )
7949     return false;
7950
7951   theFaces.push_back( curElem );
7952   theNodes.push_back( theFirstNode );
7953   theNodes.push_back( theSecondNode );
7954
7955   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7956   TIDSortedElemSet foundElems;
7957   bool needTheLast = ( theLastNode != 0 );
7958
7959   while ( nStart != theLastNode ) {
7960     if ( nStart == theFirstNode )
7961       return !needTheLast;
7962
7963     // find all free border faces sharing form nStart
7964
7965     list< const SMDS_MeshElement* > curElemList;
7966     list< const SMDS_MeshNode* >    nStartList;
7967     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7968     while ( invElemIt->more() ) {
7969       const SMDS_MeshElement* e = invElemIt->next();
7970       if ( e == curElem || foundElems.insert( e ).second ) {
7971         // get nodes
7972         int iNode = 0, nbNodes = e->NbNodes();
7973         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7974         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7975                       SMDS_MeshElement::iterator() );
7976         nodes.push_back( nodes[ 0 ]);
7977
7978         // check 2 links
7979         for ( iNode = 0; iNode < nbNodes; iNode++ )
7980           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7981                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7982               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7983           {
7984             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7985             curElemList.push_back( e );
7986           }
7987       }
7988     }
7989     // analyse the found
7990
7991     int nbNewBorders = curElemList.size();
7992     if ( nbNewBorders == 0 ) {
7993       // no free border furthermore
7994       return !needTheLast;
7995     }
7996     else if ( nbNewBorders == 1 ) {
7997       // one more element found
7998       nIgnore = nStart;
7999       nStart = nStartList.front();
8000       curElem = curElemList.front();
8001       theFaces.push_back( curElem );
8002       theNodes.push_back( nStart );
8003     }
8004     else {
8005       // several continuations found
8006       list< const SMDS_MeshElement* >::iterator curElemIt;
8007       list< const SMDS_MeshNode* >::iterator nStartIt;
8008       // check if one of them reached the last node
8009       if ( needTheLast ) {
8010         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8011              curElemIt!= curElemList.end();
8012              curElemIt++, nStartIt++ )
8013           if ( *nStartIt == theLastNode ) {
8014             theFaces.push_back( *curElemIt );
8015             theNodes.push_back( *nStartIt );
8016             return true;
8017           }
8018       }
8019       // find the best free border by the continuations
8020       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8021       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8022       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8023            curElemIt!= curElemList.end();
8024            curElemIt++, nStartIt++ )
8025       {
8026         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8027         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8028         // find one more free border
8029         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8030           cNL->clear();
8031           cFL->clear();
8032         }
8033         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8034           // choice: clear a worse one
8035           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8036           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8037           contNodes[ iWorse ].clear();
8038           contFaces[ iWorse ].clear();
8039         }
8040       }
8041       if ( contNodes[0].empty() && contNodes[1].empty() )
8042         return false;
8043
8044       // push_back the best free border
8045       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8046       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8047       theNodes.pop_back(); // remove nIgnore
8048       theNodes.pop_back(); // remove nStart
8049       theFaces.pop_back(); // remove curElem
8050       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8051       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8052       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8053       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8054       return true;
8055
8056     } // several continuations found
8057   } // while ( nStart != theLastNode )
8058
8059   return true;
8060 }
8061
8062 //=======================================================================
8063 //function : CheckFreeBorderNodes
8064 //purpose  : Return true if the tree nodes are on a free border
8065 //=======================================================================
8066
8067 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8068                                             const SMDS_MeshNode* theNode2,
8069                                             const SMDS_MeshNode* theNode3)
8070 {
8071   list< const SMDS_MeshNode* > nodes;
8072   list< const SMDS_MeshElement* > faces;
8073   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8074 }
8075
8076 //=======================================================================
8077 //function : SewFreeBorder
8078 //purpose  :
8079 //warning  : for border-to-side sewing theSideSecondNode is considered as
8080 //           the last side node and theSideThirdNode is not used
8081 //=======================================================================
8082
8083 SMESH_MeshEditor::Sew_Error
8084 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8085                                  const SMDS_MeshNode* theBordSecondNode,
8086                                  const SMDS_MeshNode* theBordLastNode,
8087                                  const SMDS_MeshNode* theSideFirstNode,
8088                                  const SMDS_MeshNode* theSideSecondNode,
8089                                  const SMDS_MeshNode* theSideThirdNode,
8090                                  const bool           theSideIsFreeBorder,
8091                                  const bool           toCreatePolygons,
8092                                  const bool           toCreatePolyedrs)
8093 {
8094   ClearLastCreated();
8095
8096   Sew_Error aResult = SEW_OK;
8097
8098   // ====================================
8099   //    find side nodes and elements
8100   // ====================================
8101
8102   list< const SMDS_MeshNode* >    nSide[ 2 ];
8103   list< const SMDS_MeshElement* > eSide[ 2 ];
8104   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8105   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8106
8107   // Free border 1
8108   // --------------
8109   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8110                       nSide[0], eSide[0])) {
8111     MESSAGE(" Free Border 1 not found " );
8112     aResult = SEW_BORDER1_NOT_FOUND;
8113   }
8114   if (theSideIsFreeBorder) {
8115     // Free border 2
8116     // --------------
8117     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8118                         nSide[1], eSide[1])) {
8119       MESSAGE(" Free Border 2 not found " );
8120       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8121     }
8122   }
8123   if ( aResult != SEW_OK )
8124     return aResult;
8125
8126   if (!theSideIsFreeBorder) {
8127     // Side 2
8128     // --------------
8129
8130     // -------------------------------------------------------------------------
8131     // Algo:
8132     // 1. If nodes to merge are not coincident, move nodes of the free border
8133     //    from the coord sys defined by the direction from the first to last
8134     //    nodes of the border to the correspondent sys of the side 2
8135     // 2. On the side 2, find the links most co-directed with the correspondent
8136     //    links of the free border
8137     // -------------------------------------------------------------------------
8138
8139     // 1. Since sewing may break if there are volumes to split on the side 2,
8140     //    we won't move nodes but just compute new coordinates for them
8141     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8142     TNodeXYZMap nBordXYZ;
8143     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8144     list< const SMDS_MeshNode* >::iterator nBordIt;
8145
8146     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8147     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8148     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8149     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8150     double tol2 = 1.e-8;
8151     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8152     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8153       // Need node movement.
8154
8155       // find X and Z axes to create trsf
8156       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8157       gp_Vec X = Zs ^ Zb;
8158       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8159         // Zb || Zs
8160         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8161
8162       // coord systems
8163       gp_Ax3 toBordAx( Pb1, Zb, X );
8164       gp_Ax3 fromSideAx( Ps1, Zs, X );
8165       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8166       // set trsf
8167       gp_Trsf toBordSys, fromSide2Sys;
8168       toBordSys.SetTransformation( toBordAx );
8169       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8170       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8171
8172       // move
8173       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8174         const SMDS_MeshNode* n = *nBordIt;
8175         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8176         toBordSys.Transforms( xyz );
8177         fromSide2Sys.Transforms( xyz );
8178         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8179       }
8180     }
8181     else {
8182       // just insert nodes XYZ in the nBordXYZ map
8183       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8184         const SMDS_MeshNode* n = *nBordIt;
8185         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8186       }
8187     }
8188
8189     // 2. On the side 2, find the links most co-directed with the correspondent
8190     //    links of the free border
8191
8192     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8193     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8194     sideNodes.push_back( theSideFirstNode );
8195
8196     bool hasVolumes = false;
8197     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8198     set<long> foundSideLinkIDs, checkedLinkIDs;
8199     SMDS_VolumeTool volume;
8200     //const SMDS_MeshNode* faceNodes[ 4 ];
8201
8202     const SMDS_MeshNode*    sideNode;
8203     const SMDS_MeshElement* sideElem  = 0;
8204     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8205     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8206     nBordIt = bordNodes.begin();
8207     nBordIt++;
8208     // border node position and border link direction to compare with
8209     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8210     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8211     // choose next side node by link direction or by closeness to
8212     // the current border node:
8213     bool searchByDir = ( *nBordIt != theBordLastNode );
8214     do {
8215       // find the next node on the Side 2
8216       sideNode = 0;
8217       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8218       long linkID;
8219       checkedLinkIDs.clear();
8220       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8221
8222       // loop on inverse elements of current node (prevSideNode) on the Side 2
8223       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8224       while ( invElemIt->more() )
8225       {
8226         const SMDS_MeshElement* elem = invElemIt->next();
8227         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8228         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8229         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8230         bool isVolume = volume.Set( elem );
8231         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8232         if ( isVolume ) // --volume
8233           hasVolumes = true;
8234         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8235           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8236           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8237           while ( nIt->more() ) {
8238             nodes[ iNode ] = cast2Node( nIt->next() );
8239             if ( nodes[ iNode++ ] == prevSideNode )
8240               iPrevNode = iNode - 1;
8241           }
8242           // there are 2 links to check
8243           nbNodes = 2;
8244         }
8245         else // --edge
8246           continue;
8247         // loop on links, to be precise, on the second node of links
8248         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8249           const SMDS_MeshNode* n = nodes[ iNode ];
8250           if ( isVolume ) {
8251             if ( !volume.IsLinked( n, prevSideNode ))
8252               continue;
8253           }
8254           else {
8255             if ( iNode ) // a node before prevSideNode
8256               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8257             else         // a node after prevSideNode
8258               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8259           }
8260           // check if this link was already used
8261           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8262           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8263           if (!isJustChecked &&
8264               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8265           {
8266             // test a link geometrically
8267             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8268             bool linkIsBetter = false;
8269             double dot = 0.0, dist = 0.0;
8270             if ( searchByDir ) { // choose most co-directed link
8271               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8272               linkIsBetter = ( dot > maxDot );
8273             }
8274             else { // choose link with the node closest to bordPos
8275               dist = ( nextXYZ - bordPos ).SquareModulus();
8276               linkIsBetter = ( dist < minDist );
8277             }
8278             if ( linkIsBetter ) {
8279               maxDot = dot;
8280               minDist = dist;
8281               linkID = iLink;
8282               sideNode = n;
8283               sideElem = elem;
8284             }
8285           }
8286         }
8287       } // loop on inverse elements of prevSideNode
8288
8289       if ( !sideNode ) {
8290         MESSAGE(" Can't find path by links of the Side 2 ");
8291         return SEW_BAD_SIDE_NODES;
8292       }
8293       sideNodes.push_back( sideNode );
8294       sideElems.push_back( sideElem );
8295       foundSideLinkIDs.insert ( linkID );
8296       prevSideNode = sideNode;
8297
8298       if ( *nBordIt == theBordLastNode )
8299         searchByDir = false;
8300       else {
8301         // find the next border link to compare with
8302         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8303         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8304         // move to next border node if sideNode is before forward border node (bordPos)
8305         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8306           prevBordNode = *nBordIt;
8307           nBordIt++;
8308           bordPos = nBordXYZ[ *nBordIt ];
8309           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8310           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8311         }
8312       }
8313     }
8314     while ( sideNode != theSideSecondNode );
8315
8316     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8317       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8318       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8319     }
8320   } // end nodes search on the side 2
8321
8322   // ============================
8323   // sew the border to the side 2
8324   // ============================
8325
8326   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8327   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8328
8329   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8330   if ( toMergeConformal && toCreatePolygons )
8331   {
8332     // do not merge quadrangles if polygons are OK (IPAL0052824)
8333     eIt[0] = eSide[0].begin();
8334     eIt[1] = eSide[1].begin();
8335     bool allQuads[2] = { true, true };
8336     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8337       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8338         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8339     }
8340     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8341   }
8342
8343   TListOfListOfNodes nodeGroupsToMerge;
8344   if (( toMergeConformal ) ||
8345       ( theSideIsFreeBorder && !theSideThirdNode )) {
8346
8347     // all nodes are to be merged
8348
8349     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8350          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8351          nIt[0]++, nIt[1]++ )
8352     {
8353       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8354       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8355       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8356     }
8357   }
8358   else {
8359
8360     // insert new nodes into the border and the side to get equal nb of segments
8361
8362     // get normalized parameters of nodes on the borders
8363     vector< double > param[ 2 ];
8364     param[0].resize( maxNbNodes );
8365     param[1].resize( maxNbNodes );
8366     int iNode, iBord;
8367     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8368       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8369       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8370       const SMDS_MeshNode* nPrev = *nIt;
8371       double bordLength = 0;
8372       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8373         const SMDS_MeshNode* nCur = *nIt;
8374         gp_XYZ segment (nCur->X() - nPrev->X(),
8375                         nCur->Y() - nPrev->Y(),
8376                         nCur->Z() - nPrev->Z());
8377         double segmentLen = segment.Modulus();
8378         bordLength += segmentLen;
8379         param[ iBord ][ iNode ] = bordLength;
8380         nPrev = nCur;
8381       }
8382       // normalize within [0,1]
8383       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8384         param[ iBord ][ iNode ] /= bordLength;
8385       }
8386     }
8387
8388     // loop on border segments
8389     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8390     int i[ 2 ] = { 0, 0 };
8391     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8392     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8393
8394     TElemOfNodeListMap insertMap;
8395     TElemOfNodeListMap::iterator insertMapIt;
8396     // insertMap is
8397     // key:   elem to insert nodes into
8398     // value: 2 nodes to insert between + nodes to be inserted
8399     do {
8400       bool next[ 2 ] = { false, false };
8401
8402       // find min adjacent segment length after sewing
8403       double nextParam = 10., prevParam = 0;
8404       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8405         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8406           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8407         if ( i[ iBord ] > 0 )
8408           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8409       }
8410       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8411       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8412       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8413
8414       // choose to insert or to merge nodes
8415       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8416       if ( Abs( du ) <= minSegLen * 0.2 ) {
8417         // merge
8418         // ------
8419         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8420         const SMDS_MeshNode* n0 = *nIt[0];
8421         const SMDS_MeshNode* n1 = *nIt[1];
8422         nodeGroupsToMerge.back().push_back( n1 );
8423         nodeGroupsToMerge.back().push_back( n0 );
8424         // position of node of the border changes due to merge
8425         param[ 0 ][ i[0] ] += du;
8426         // move n1 for the sake of elem shape evaluation during insertion.
8427         // n1 will be removed by MergeNodes() anyway
8428         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8429         next[0] = next[1] = true;
8430       }
8431       else {
8432         // insert
8433         // ------
8434         int intoBord = ( du < 0 ) ? 0 : 1;
8435         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8436         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8437         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8438         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8439         if ( intoBord == 1 ) {
8440           // move node of the border to be on a link of elem of the side
8441           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8442           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8443           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8444           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8445           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8446         }
8447         insertMapIt = insertMap.find( elem );
8448         bool  notFound = ( insertMapIt == insertMap.end() );
8449         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8450         if ( otherLink ) {
8451           // insert into another link of the same element:
8452           // 1. perform insertion into the other link of the elem
8453           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8454           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8455           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8456           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8457           // 2. perform insertion into the link of adjacent faces
8458           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8459             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8460           }
8461           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8462             InsertNodesIntoLink( seg, n12, n22, nodeList );
8463           }
8464           if (toCreatePolyedrs) {
8465             // perform insertion into the links of adjacent volumes
8466             UpdateVolumes(n12, n22, nodeList);
8467           }
8468           // 3. find an element appeared on n1 and n2 after the insertion
8469           insertMap.erase( elem );
8470           elem = findAdjacentFace( n1, n2, 0 );
8471         }
8472         if ( notFound || otherLink ) {
8473           // add element and nodes of the side into the insertMap
8474           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8475           (*insertMapIt).second.push_back( n1 );
8476           (*insertMapIt).second.push_back( n2 );
8477         }
8478         // add node to be inserted into elem
8479         (*insertMapIt).second.push_back( nIns );
8480         next[ 1 - intoBord ] = true;
8481       }
8482
8483       // go to the next segment
8484       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8485         if ( next[ iBord ] ) {
8486           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8487             eIt[ iBord ]++;
8488           nPrev[ iBord ] = *nIt[ iBord ];
8489           nIt[ iBord ]++; i[ iBord ]++;
8490         }
8491       }
8492     }
8493     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8494
8495     // perform insertion of nodes into elements
8496
8497     for (insertMapIt = insertMap.begin();
8498          insertMapIt != insertMap.end();
8499          insertMapIt++ )
8500     {
8501       const SMDS_MeshElement* elem = (*insertMapIt).first;
8502       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8503       if ( nodeList.size() < 3 ) continue;
8504       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8505       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8506
8507       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8508
8509       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8510         InsertNodesIntoLink( seg, n1, n2, nodeList );
8511       }
8512
8513       if ( !theSideIsFreeBorder ) {
8514         // look for and insert nodes into the faces adjacent to elem
8515         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8516           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8517         }
8518       }
8519       if (toCreatePolyedrs) {
8520         // perform insertion into the links of adjacent volumes
8521         UpdateVolumes(n1, n2, nodeList);
8522       }
8523     }
8524   } // end: insert new nodes
8525
8526   MergeNodes ( nodeGroupsToMerge );
8527
8528
8529   // Remove coincident segments
8530
8531   // get new segments
8532   TIDSortedElemSet segments;
8533   SMESH_SequenceOfElemPtr newFaces;
8534   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8535   {
8536     if ( !myLastCreatedElems[i] ) continue;
8537     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8538       segments.insert( segments.end(), myLastCreatedElems[i] );
8539     else
8540       newFaces.push_back( myLastCreatedElems[i] );
8541   }
8542   // get segments adjacent to merged nodes
8543   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8544   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8545   {
8546     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8547     if ( nodes.front()->IsNull() ) continue;
8548     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8549     while ( segIt->more() )
8550       segments.insert( segIt->next() );
8551   }
8552
8553   // find coincident
8554   TListOfListOfElementsID equalGroups;
8555   if ( !segments.empty() )
8556     FindEqualElements( segments, equalGroups );
8557   if ( !equalGroups.empty() )
8558   {
8559     // remove from segments those that will be removed
8560     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8561     for ( ; itGroups != equalGroups.end(); ++itGroups )
8562     {
8563       list< int >& group = *itGroups;
8564       list< int >::iterator id = group.begin();
8565       for ( ++id; id != group.end(); ++id )
8566         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8567           segments.erase( seg );
8568     }
8569     // remove equal segments
8570     MergeElements( equalGroups );
8571
8572     // restore myLastCreatedElems
8573     myLastCreatedElems = newFaces;
8574     TIDSortedElemSet::iterator seg = segments.begin();
8575     for ( ; seg != segments.end(); ++seg )
8576       myLastCreatedElems.push_back( *seg );
8577   }
8578
8579   return aResult;
8580 }
8581
8582 //=======================================================================
8583 //function : InsertNodesIntoLink
8584 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8585 //           and theBetweenNode2 and split theElement
8586 //=======================================================================
8587
8588 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8589                                            const SMDS_MeshNode*        theBetweenNode1,
8590                                            const SMDS_MeshNode*        theBetweenNode2,
8591                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8592                                            const bool                  toCreatePoly)
8593 {
8594   if ( !theElement ) return;
8595
8596   SMESHDS_Mesh *aMesh = GetMeshDS();
8597   vector<const SMDS_MeshElement*> newElems;
8598
8599   if ( theElement->GetType() == SMDSAbs_Edge )
8600   {
8601     theNodesToInsert.push_front( theBetweenNode1 );
8602     theNodesToInsert.push_back ( theBetweenNode2 );
8603     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8604     const SMDS_MeshNode* n1 = *n;
8605     for ( ++n; n != theNodesToInsert.end(); ++n )
8606     {
8607       const SMDS_MeshNode* n2 = *n;
8608       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8609         AddToSameGroups( seg, theElement, aMesh );
8610       else
8611         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8612       n1 = n2;
8613     }
8614     theNodesToInsert.pop_front();
8615     theNodesToInsert.pop_back();
8616
8617     if ( theElement->IsQuadratic() ) // add a not split part
8618     {
8619       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8620                                           theElement->end_nodes() );
8621       int iOther = 0, nbN = nodes.size();
8622       for ( ; iOther < nbN; ++iOther )
8623         if ( nodes[iOther] != theBetweenNode1 &&
8624              nodes[iOther] != theBetweenNode2 )
8625           break;
8626       if      ( iOther == 0 )
8627       {
8628         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8629           AddToSameGroups( seg, theElement, aMesh );
8630         else
8631           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8632       }
8633       else if ( iOther == 2 )
8634       {
8635         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8636           AddToSameGroups( seg, theElement, aMesh );
8637         else
8638           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8639       }
8640     }
8641     // treat new elements
8642     for ( size_t i = 0; i < newElems.size(); ++i )
8643       if ( newElems[i] )
8644       {
8645         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8646         myLastCreatedElems.push_back( newElems[i] );
8647       }
8648     ReplaceElemInGroups( theElement, newElems, aMesh );
8649     aMesh->RemoveElement( theElement );
8650     return;
8651
8652   } // if ( theElement->GetType() == SMDSAbs_Edge )
8653
8654   const SMDS_MeshElement* theFace = theElement;
8655   if ( theFace->GetType() != SMDSAbs_Face ) return;
8656
8657   // find indices of 2 link nodes and of the rest nodes
8658   int iNode = 0, il1, il2, i3, i4;
8659   il1 = il2 = i3 = i4 = -1;
8660   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8661
8662   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8663   while ( nodeIt->more() ) {
8664     const SMDS_MeshNode* n = nodeIt->next();
8665     if ( n == theBetweenNode1 )
8666       il1 = iNode;
8667     else if ( n == theBetweenNode2 )
8668       il2 = iNode;
8669     else if ( i3 < 0 )
8670       i3 = iNode;
8671     else
8672       i4 = iNode;
8673     nodes[ iNode++ ] = n;
8674   }
8675   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8676     return ;
8677
8678   // arrange link nodes to go one after another regarding the face orientation
8679   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8680   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8681   if ( reverse ) {
8682     iNode = il1;
8683     il1 = il2;
8684     il2 = iNode;
8685     aNodesToInsert.reverse();
8686   }
8687   // check that not link nodes of a quadrangles are in good order
8688   int nbFaceNodes = theFace->NbNodes();
8689   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8690     iNode = i3;
8691     i3 = i4;
8692     i4 = iNode;
8693   }
8694
8695   if (toCreatePoly || theFace->IsPoly()) {
8696
8697     iNode = 0;
8698     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8699
8700     // add nodes of face up to first node of link
8701     bool isFLN = false;
8702     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703     while ( nodeIt->more() && !isFLN ) {
8704       const SMDS_MeshNode* n = nodeIt->next();
8705       poly_nodes[iNode++] = n;
8706       isFLN = ( n == nodes[il1] );
8707     }
8708     // add nodes to insert
8709     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710     for (; nIt != aNodesToInsert.end(); nIt++) {
8711       poly_nodes[iNode++] = *nIt;
8712     }
8713     // add nodes of face starting from last node of link
8714     while ( nodeIt->more() ) {
8715       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8716       poly_nodes[iNode++] = n;
8717     }
8718
8719     // make a new face
8720     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8721   }
8722
8723   else if ( !theFace->IsQuadratic() )
8724   {
8725     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8726     int nbLinkNodes = 2 + aNodesToInsert.size();
8727     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8728     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8729     linkNodes[ 0 ] = nodes[ il1 ];
8730     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8731     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8733       linkNodes[ iNode++ ] = *nIt;
8734     }
8735     // decide how to split a quadrangle: compare possible variants
8736     // and choose which of splits to be a quadrangle
8737     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8738     if ( nbFaceNodes == 3 ) {
8739       iBestQuad = nbSplits;
8740       i4 = i3;
8741     }
8742     else if ( nbFaceNodes == 4 ) {
8743       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8744       double aBestRate = DBL_MAX;
8745       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8746         i1 = 0; i2 = 1;
8747         double aBadRate = 0;
8748         // evaluate elements quality
8749         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8750           if ( iSplit == iQuad ) {
8751             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8752                                    linkNodes[ i2++ ],
8753                                    nodes[ i3 ],
8754                                    nodes[ i4 ]);
8755             aBadRate += getBadRate( &quad, aCrit );
8756           }
8757           else {
8758             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8759                                    linkNodes[ i2++ ],
8760                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8761             aBadRate += getBadRate( &tria, aCrit );
8762           }
8763         }
8764         // choice
8765         if ( aBadRate < aBestRate ) {
8766           iBestQuad = iQuad;
8767           aBestRate = aBadRate;
8768         }
8769       }
8770     }
8771
8772     // create new elements
8773     i1 = 0; i2 = 1;
8774     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8775     {
8776       if ( iSplit == iBestQuad )
8777         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8778                                             linkNodes[ i2++ ],
8779                                             nodes[ i3 ],
8780                                             nodes[ i4 ]));
8781       else
8782         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8783                                             linkNodes[ i2++ ],
8784                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8785     }
8786
8787     const SMDS_MeshNode* newNodes[ 4 ];
8788     newNodes[ 0 ] = linkNodes[ i1 ];
8789     newNodes[ 1 ] = linkNodes[ i2 ];
8790     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8791     newNodes[ 3 ] = nodes[ i4 ];
8792     if (iSplit == iBestQuad)
8793       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8794     else
8795       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8796
8797   } // end if(!theFace->IsQuadratic())
8798
8799   else { // theFace is quadratic
8800     // we have to split theFace on simple triangles and one simple quadrangle
8801     int tmp = il1/2;
8802     int nbshift = tmp*2;
8803     // shift nodes in nodes[] by nbshift
8804     int i,j;
8805     for(i=0; i<nbshift; i++) {
8806       const SMDS_MeshNode* n = nodes[0];
8807       for(j=0; j<nbFaceNodes-1; j++) {
8808         nodes[j] = nodes[j+1];
8809       }
8810       nodes[nbFaceNodes-1] = n;
8811     }
8812     il1 = il1 - nbshift;
8813     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8814     //   n0      n1     n2    n0      n1     n2
8815     //     +-----+-----+        +-----+-----+
8816     //      \         /         |           |
8817     //       \       /          |           |
8818     //      n5+     +n3       n7+           +n3
8819     //         \   /            |           |
8820     //          \ /             |           |
8821     //           +              +-----+-----+
8822     //           n4           n6      n5     n4
8823
8824     // create new elements
8825     int n1,n2,n3;
8826     if ( nbFaceNodes == 6 ) { // quadratic triangle
8827       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8828       if ( theFace->IsMediumNode(nodes[il1]) ) {
8829         // create quadrangle
8830         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8831         n1 = 1;
8832         n2 = 2;
8833         n3 = 3;
8834       }
8835       else {
8836         // create quadrangle
8837         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8838         n1 = 0;
8839         n2 = 1;
8840         n3 = 5;
8841       }
8842     }
8843     else { // nbFaceNodes==8 - quadratic quadrangle
8844       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8845       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8846       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8847       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8848         // create quadrangle
8849         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8850         n1 = 1;
8851         n2 = 2;
8852         n3 = 3;
8853       }
8854       else {
8855         // create quadrangle
8856         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8857         n1 = 0;
8858         n2 = 1;
8859         n3 = 7;
8860       }
8861     }
8862     // create needed triangles using n1,n2,n3 and inserted nodes
8863     int nbn = 2 + aNodesToInsert.size();
8864     vector<const SMDS_MeshNode*> aNodes(nbn);
8865     aNodes[0    ] = nodes[n1];
8866     aNodes[nbn-1] = nodes[n2];
8867     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8868     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8869       aNodes[iNode++] = *nIt;
8870     }
8871     for ( i = 1; i < nbn; i++ )
8872       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8873   }
8874
8875   // remove the old face
8876   for ( size_t i = 0; i < newElems.size(); ++i )
8877     if ( newElems[i] )
8878     {
8879       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8880       myLastCreatedElems.push_back( newElems[i] );
8881     }
8882   ReplaceElemInGroups( theFace, newElems, aMesh );
8883   aMesh->RemoveElement(theFace);
8884
8885 } // InsertNodesIntoLink()
8886
8887 //=======================================================================
8888 //function : UpdateVolumes
8889 //purpose  :
8890 //=======================================================================
8891
8892 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8893                                       const SMDS_MeshNode*        theBetweenNode2,
8894                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8895 {
8896   ClearLastCreated();
8897
8898   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900     const SMDS_MeshElement* elem = invElemIt->next();
8901
8902     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903     SMDS_VolumeTool aVolume (elem);
8904     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8905       continue;
8906
8907     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908     int iface, nbFaces = aVolume.NbFaces();
8909     vector<const SMDS_MeshNode *> poly_nodes;
8910     vector<int> quantities (nbFaces);
8911
8912     for (iface = 0; iface < nbFaces; iface++) {
8913       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8916
8917       for (int inode = 0; inode < nbFaceNodes; inode++) {
8918         poly_nodes.push_back(faceNodes[inode]);
8919
8920         if (nbInserted == 0) {
8921           if (faceNodes[inode] == theBetweenNode1) {
8922             if (faceNodes[inode + 1] == theBetweenNode2) {
8923               nbInserted = theNodesToInsert.size();
8924
8925               // add nodes to insert
8926               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927               for (; nIt != theNodesToInsert.end(); nIt++) {
8928                 poly_nodes.push_back(*nIt);
8929               }
8930             }
8931           }
8932           else if (faceNodes[inode] == theBetweenNode2) {
8933             if (faceNodes[inode + 1] == theBetweenNode1) {
8934               nbInserted = theNodesToInsert.size();
8935
8936               // add nodes to insert in reversed order
8937               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8938               nIt--;
8939               for (; nIt != theNodesToInsert.begin(); nIt--) {
8940                 poly_nodes.push_back(*nIt);
8941               }
8942               poly_nodes.push_back(*nIt);
8943             }
8944           }
8945           else {
8946           }
8947         }
8948       }
8949       quantities[iface] = nbFaceNodes + nbInserted;
8950     }
8951
8952     // Replace the volume
8953     SMESHDS_Mesh *aMesh = GetMeshDS();
8954
8955     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8956     {
8957       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8958       myLastCreatedElems.push_back( newElem );
8959       ReplaceElemInGroups( elem, newElem, aMesh );
8960     }
8961     aMesh->RemoveElement( elem );
8962   }
8963 }
8964
8965 namespace
8966 {
8967   //================================================================================
8968   /*!
8969    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8970    */
8971   //================================================================================
8972
8973   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8974                            vector<const SMDS_MeshNode *> & nodes,
8975                            vector<int> &                   nbNodeInFaces )
8976   {
8977     nodes.clear();
8978     nbNodeInFaces.clear();
8979     SMDS_VolumeTool vTool ( elem );
8980     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8981     {
8982       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8983       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8984       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8985     }
8986   }
8987 }
8988
8989 //=======================================================================
8990 /*!
8991  * \brief Convert elements contained in a sub-mesh to quadratic
8992  * \return int - nb of checked elements
8993  */
8994 //=======================================================================
8995
8996 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8997                                              SMESH_MesherHelper& theHelper,
8998                                              const bool          theForce3d)
8999 {
9000   //MESSAGE("convertElemToQuadratic");
9001   int nbElem = 0;
9002   if( !theSm ) return nbElem;
9003
9004   vector<int> nbNodeInFaces;
9005   vector<const SMDS_MeshNode *> nodes;
9006   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9007   while(ElemItr->more())
9008   {
9009     nbElem++;
9010     const SMDS_MeshElement* elem = ElemItr->next();
9011     if( !elem ) continue;
9012
9013     // analyse a necessity of conversion
9014     const SMDSAbs_ElementType aType = elem->GetType();
9015     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9016       continue;
9017     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9018     bool hasCentralNodes = false;
9019     if ( elem->IsQuadratic() )
9020     {
9021       bool alreadyOK;
9022       switch ( aGeomType ) {
9023       case SMDSEntity_Quad_Triangle:
9024       case SMDSEntity_Quad_Quadrangle:
9025       case SMDSEntity_Quad_Hexa:
9026       case SMDSEntity_Quad_Penta:
9027         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9028
9029       case SMDSEntity_BiQuad_Triangle:
9030       case SMDSEntity_BiQuad_Quadrangle:
9031       case SMDSEntity_TriQuad_Hexa:
9032       case SMDSEntity_BiQuad_Penta:
9033         alreadyOK = theHelper.GetIsBiQuadratic();
9034         hasCentralNodes = true;
9035         break;
9036       default:
9037         alreadyOK = true;
9038       }
9039       // take into account already present medium nodes
9040       switch ( aType ) {
9041       case SMDSAbs_Volume:
9042         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9043       case SMDSAbs_Face:
9044         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9045       case SMDSAbs_Edge:
9046         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9047       default:;
9048       }
9049       if ( alreadyOK )
9050         continue;
9051     }
9052     // get elem data needed to re-create it
9053     //
9054     const int id      = elem->GetID();
9055     const int nbNodes = elem->NbCornerNodes();
9056     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9057     if ( aGeomType == SMDSEntity_Polyhedra )
9058       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9059     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9060       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9061
9062     // remove a linear element
9063     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9064
9065     // remove central nodes of biquadratic elements (biquad->quad conversion)
9066     if ( hasCentralNodes )
9067       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9068         if ( nodes[i]->NbInverseElements() == 0 )
9069           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9070
9071     const SMDS_MeshElement* NewElem = 0;
9072
9073     switch( aType )
9074     {
9075     case SMDSAbs_Edge :
9076     {
9077       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9078       break;
9079     }
9080     case SMDSAbs_Face :
9081     {
9082       switch(nbNodes)
9083       {
9084       case 3:
9085         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9086         break;
9087       case 4:
9088         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9089         break;
9090       default:
9091         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9092       }
9093       break;
9094     }
9095     case SMDSAbs_Volume :
9096     {
9097       switch( aGeomType )
9098       {
9099       case SMDSEntity_Tetra:
9100         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9101         break;
9102       case SMDSEntity_Pyramid:
9103         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9104         break;
9105       case SMDSEntity_Penta:
9106       case SMDSEntity_Quad_Penta:
9107       case SMDSEntity_BiQuad_Penta:
9108         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9109         break;
9110       case SMDSEntity_Hexa:
9111       case SMDSEntity_Quad_Hexa:
9112       case SMDSEntity_TriQuad_Hexa:
9113         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9114                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9115         break;
9116       case SMDSEntity_Hexagonal_Prism:
9117       default:
9118         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9119       }
9120       break;
9121     }
9122     default :
9123       continue;
9124     }
9125     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9126     if( NewElem && NewElem->getshapeId() < 1 )
9127       theSm->AddElement( NewElem );
9128   }
9129   return nbElem;
9130 }
9131 //=======================================================================
9132 //function : ConvertToQuadratic
9133 //purpose  :
9134 //=======================================================================
9135
9136 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9137 {
9138   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9139   SMESHDS_Mesh* meshDS = GetMeshDS();
9140
9141   SMESH_MesherHelper aHelper(*myMesh);
9142
9143   aHelper.SetIsQuadratic( true );
9144   aHelper.SetIsBiQuadratic( theToBiQuad );
9145   aHelper.SetElementsOnShape(true);
9146   aHelper.ToFixNodeParameters( true );
9147
9148   // convert elements assigned to sub-meshes
9149   int nbCheckedElems = 0;
9150   if ( myMesh->HasShapeToMesh() )
9151   {
9152     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9153     {
9154       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9155       while ( smIt->more() ) {
9156         SMESH_subMesh* sm = smIt->next();
9157         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9158           aHelper.SetSubShape( sm->GetSubShape() );
9159           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9160         }
9161       }
9162     }
9163   }
9164
9165   // convert elements NOT assigned to sub-meshes
9166   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9167   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9168   {
9169     aHelper.SetElementsOnShape(false);
9170     SMESHDS_SubMesh *smDS = 0;
9171
9172     // convert edges
9173     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9174     while( aEdgeItr->more() )
9175     {
9176       const SMDS_MeshEdge* edge = aEdgeItr->next();
9177       if ( !edge->IsQuadratic() )
9178       {
9179         int                  id = edge->GetID();
9180         const SMDS_MeshNode* n1 = edge->GetNode(0);
9181         const SMDS_MeshNode* n2 = edge->GetNode(1);
9182
9183         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9184
9185         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9186         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9187       }
9188       else
9189       {
9190         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9191       }
9192     }
9193
9194     // convert faces
9195     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9196     while( aFaceItr->more() )
9197     {
9198       const SMDS_MeshFace* face = aFaceItr->next();
9199       if ( !face ) continue;
9200       
9201       const SMDSAbs_EntityType type = face->GetEntityType();
9202       bool alreadyOK;
9203       switch( type )
9204       {
9205       case SMDSEntity_Quad_Triangle:
9206       case SMDSEntity_Quad_Quadrangle:
9207         alreadyOK = !theToBiQuad;
9208         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9209         break;
9210       case SMDSEntity_BiQuad_Triangle:
9211       case SMDSEntity_BiQuad_Quadrangle:
9212         alreadyOK = theToBiQuad;
9213         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9214         break;
9215       default: alreadyOK = false;
9216       }
9217       if ( alreadyOK )
9218         continue;
9219
9220       const int id = face->GetID();
9221       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9222
9223       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9224
9225       SMDS_MeshFace * NewFace = 0;
9226       switch( type )
9227       {
9228       case SMDSEntity_Triangle:
9229       case SMDSEntity_Quad_Triangle:
9230       case SMDSEntity_BiQuad_Triangle:
9231         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9232         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9233           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9234         break;
9235
9236       case SMDSEntity_Quadrangle:
9237       case SMDSEntity_Quad_Quadrangle:
9238       case SMDSEntity_BiQuad_Quadrangle:
9239         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9240         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9241           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9242         break;
9243
9244       default:;
9245         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9246       }
9247       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9248     }
9249
9250     // convert volumes
9251     vector<int> nbNodeInFaces;
9252     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9253     while(aVolumeItr->more())
9254     {
9255       const SMDS_MeshVolume* volume = aVolumeItr->next();
9256       if ( !volume ) continue;
9257
9258       const SMDSAbs_EntityType type = volume->GetEntityType();
9259       if ( volume->IsQuadratic() )
9260       {
9261         bool alreadyOK;
9262         switch ( type )
9263         {
9264         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9265         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9266         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9267         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9268         default:                      alreadyOK = true;
9269         }
9270         if ( alreadyOK )
9271         {
9272           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9273           continue;
9274         }
9275       }
9276       const int id = volume->GetID();
9277       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278       if ( type == SMDSEntity_Polyhedra )
9279         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9280       else if ( type == SMDSEntity_Hexagonal_Prism )
9281         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9282
9283       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9284
9285       SMDS_MeshVolume * NewVolume = 0;
9286       switch ( type )
9287       {
9288       case SMDSEntity_Tetra:
9289         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9290         break;
9291       case SMDSEntity_Hexa:
9292       case SMDSEntity_Quad_Hexa:
9293       case SMDSEntity_TriQuad_Hexa:
9294         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297           if ( nodes[i]->NbInverseElements() == 0 )
9298             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9299         break;
9300       case SMDSEntity_Pyramid:
9301         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302                                       nodes[3], nodes[4], id, theForce3d);
9303         break;
9304       case SMDSEntity_Penta:
9305       case SMDSEntity_Quad_Penta:
9306       case SMDSEntity_BiQuad_Penta:
9307         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9308                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9309         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9310           if ( nodes[i]->NbInverseElements() == 0 )
9311             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9312         break;
9313       case SMDSEntity_Hexagonal_Prism:
9314       default:
9315         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9316       }
9317       ReplaceElemInGroups(volume, NewVolume, meshDS);
9318     }
9319   }
9320
9321   if ( !theForce3d )
9322   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9323     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9324     // aHelper.FixQuadraticElements(myError);
9325     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9326   }
9327 }
9328
9329 //================================================================================
9330 /*!
9331  * \brief Makes given elements quadratic
9332  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9333  *  \param theElements - elements to make quadratic
9334  */
9335 //================================================================================
9336
9337 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9338                                           TIDSortedElemSet& theElements,
9339                                           const bool        theToBiQuad)
9340 {
9341   if ( theElements.empty() ) return;
9342
9343   // we believe that all theElements are of the same type
9344   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9345
9346   // get all nodes shared by theElements
9347   TIDSortedNodeSet allNodes;
9348   TIDSortedElemSet::iterator eIt = theElements.begin();
9349   for ( ; eIt != theElements.end(); ++eIt )
9350     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9351
9352   // complete theElements with elements of lower dim whose all nodes are in allNodes
9353
9354   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9355   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9356   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9357   for ( ; nIt != allNodes.end(); ++nIt )
9358   {
9359     const SMDS_MeshNode* n = *nIt;
9360     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9361     while ( invIt->more() )
9362     {
9363       const SMDS_MeshElement*      e = invIt->next();
9364       const SMDSAbs_ElementType type = e->GetType();
9365       if ( e->IsQuadratic() )
9366       {
9367         quadAdjacentElems[ type ].insert( e );
9368
9369         bool alreadyOK;
9370         switch ( e->GetEntityType() ) {
9371         case SMDSEntity_Quad_Triangle:
9372         case SMDSEntity_Quad_Quadrangle:
9373         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9374         case SMDSEntity_BiQuad_Triangle:
9375         case SMDSEntity_BiQuad_Quadrangle:
9376         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9377         default:                           alreadyOK = true;
9378         }
9379         if ( alreadyOK )
9380           continue;
9381       }
9382       if ( type >= elemType )
9383         continue; // same type or more complex linear element
9384
9385       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9386         continue; // e is already checked
9387
9388       // check nodes
9389       bool allIn = true;
9390       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9391       while ( nodeIt->more() && allIn )
9392         allIn = allNodes.count( nodeIt->next() );
9393       if ( allIn )
9394         theElements.insert(e );
9395     }
9396   }
9397
9398   SMESH_MesherHelper helper(*myMesh);
9399   helper.SetIsQuadratic( true );
9400   helper.SetIsBiQuadratic( theToBiQuad );
9401
9402   // add links of quadratic adjacent elements to the helper
9403
9404   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9405     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9406           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9407     {
9408       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9409     }
9410   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9411     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9412           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9413     {
9414       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9415     }
9416   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9417     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9418           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9419     {
9420       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9421     }
9422
9423   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9424
9425   SMESHDS_Mesh*  meshDS = GetMeshDS();
9426   SMESHDS_SubMesh* smDS = 0;
9427   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9428   {
9429     const SMDS_MeshElement* elem = *eIt;
9430
9431     bool alreadyOK;
9432     int nbCentralNodes = 0;
9433     switch ( elem->GetEntityType() ) {
9434       // linear convertible
9435     case SMDSEntity_Edge:
9436     case SMDSEntity_Triangle:
9437     case SMDSEntity_Quadrangle:
9438     case SMDSEntity_Tetra:
9439     case SMDSEntity_Pyramid:
9440     case SMDSEntity_Hexa:
9441     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9442       // quadratic that can become bi-quadratic
9443     case SMDSEntity_Quad_Triangle:
9444     case SMDSEntity_Quad_Quadrangle:
9445     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9446       // bi-quadratic
9447     case SMDSEntity_BiQuad_Triangle:
9448     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9449     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9450       // the rest
9451     default:                           alreadyOK = true;
9452     }
9453     if ( alreadyOK ) continue;
9454
9455     const SMDSAbs_ElementType type = elem->GetType();
9456     const int                   id = elem->GetID();
9457     const int              nbNodes = elem->NbCornerNodes();
9458     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9459
9460     helper.SetSubShape( elem->getshapeId() );
9461
9462     if ( !smDS || !smDS->Contains( elem ))
9463       smDS = meshDS->MeshElements( elem->getshapeId() );
9464     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9465
9466     SMDS_MeshElement * newElem = 0;
9467     switch( nbNodes )
9468     {
9469     case 4: // cases for most frequently used element types go first (for optimization)
9470       if ( type == SMDSAbs_Volume )
9471         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9472       else
9473         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474       break;
9475     case 8:
9476       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9477                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9478       break;
9479     case 3:
9480       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9481       break;
9482     case 2:
9483       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9484       break;
9485     case 5:
9486       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487                                  nodes[4], id, theForce3d);
9488       break;
9489     case 6:
9490       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9491                                  nodes[4], nodes[5], id, theForce3d);
9492       break;
9493     default:;
9494     }
9495     ReplaceElemInGroups( elem, newElem, meshDS);
9496     if( newElem && smDS )
9497       smDS->AddElement( newElem );
9498
9499     // remove central nodes
9500     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9501       if ( nodes[i]->NbInverseElements() == 0 )
9502         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9503
9504   } // loop on theElements
9505
9506   if ( !theForce3d )
9507   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9508     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9509     // helper.FixQuadraticElements( myError );
9510     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9511   }
9512 }
9513
9514 //=======================================================================
9515 /*!
9516  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9517  * \return int - nb of checked elements
9518  */
9519 //=======================================================================
9520
9521 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9522                                      SMDS_ElemIteratorPtr theItr,
9523                                      const int            theShapeID)
9524 {
9525   int nbElem = 0;
9526   SMESHDS_Mesh* meshDS = GetMeshDS();
9527   ElemFeatures elemType;
9528   vector<const SMDS_MeshNode *> nodes;
9529
9530   while( theItr->more() )
9531   {
9532     const SMDS_MeshElement* elem = theItr->next();
9533     nbElem++;
9534     if( elem && elem->IsQuadratic())
9535     {
9536       // get elem data
9537       int nbCornerNodes = elem->NbCornerNodes();
9538       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9539
9540       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9541
9542       //remove a quadratic element
9543       if ( !theSm || !theSm->Contains( elem ))
9544         theSm = meshDS->MeshElements( elem->getshapeId() );
9545       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9546
9547       // remove medium nodes
9548       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9549         if ( nodes[i]->NbInverseElements() == 0 )
9550           meshDS->RemoveFreeNode( nodes[i], theSm );
9551
9552       // add a linear element
9553       nodes.resize( nbCornerNodes );
9554       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9555       ReplaceElemInGroups(elem, newElem, meshDS);
9556       if( theSm && newElem )
9557         theSm->AddElement( newElem );
9558     }
9559   }
9560   return nbElem;
9561 }
9562
9563 //=======================================================================
9564 //function : ConvertFromQuadratic
9565 //purpose  :
9566 //=======================================================================
9567
9568 bool SMESH_MeshEditor::ConvertFromQuadratic()
9569 {
9570   int nbCheckedElems = 0;
9571   if ( myMesh->HasShapeToMesh() )
9572   {
9573     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9574     {
9575       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9576       while ( smIt->more() ) {
9577         SMESH_subMesh* sm = smIt->next();
9578         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9579           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9580       }
9581     }
9582   }
9583
9584   int totalNbElems =
9585     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9586   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9587   {
9588     SMESHDS_SubMesh *aSM = 0;
9589     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9590   }
9591
9592   return true;
9593 }
9594
9595 namespace
9596 {
9597   //================================================================================
9598   /*!
9599    * \brief Return true if all medium nodes of the element are in the node set
9600    */
9601   //================================================================================
9602
9603   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9604   {
9605     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9606       if ( !nodeSet.count( elem->GetNode(i) ))
9607         return false;
9608     return true;
9609   }
9610 }
9611
9612 //================================================================================
9613 /*!
9614  * \brief Makes given elements linear
9615  */
9616 //================================================================================
9617
9618 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9619 {
9620   if ( theElements.empty() ) return;
9621
9622   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9623   set<int> mediumNodeIDs;
9624   TIDSortedElemSet::iterator eIt = theElements.begin();
9625   for ( ; eIt != theElements.end(); ++eIt )
9626   {
9627     const SMDS_MeshElement* e = *eIt;
9628     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9629       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9630   }
9631
9632   // replace given elements by linear ones
9633   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9634   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9635
9636   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9637   // except those elements sharing medium nodes of quadratic element whose medium nodes
9638   // are not all in mediumNodeIDs
9639
9640   // get remaining medium nodes
9641   TIDSortedNodeSet mediumNodes;
9642   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9643   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9644     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9645       mediumNodes.insert( mediumNodes.end(), n );
9646
9647   // find more quadratic elements to convert
9648   TIDSortedElemSet moreElemsToConvert;
9649   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9650   for ( ; nIt != mediumNodes.end(); ++nIt )
9651   {
9652     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9653     while ( invIt->more() )
9654     {
9655       const SMDS_MeshElement* e = invIt->next();
9656       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9657       {
9658         // find a more complex element including e and
9659         // whose medium nodes are not in mediumNodes
9660         bool complexFound = false;
9661         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9662         {
9663           SMDS_ElemIteratorPtr invIt2 =
9664             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9665           while ( invIt2->more() )
9666           {
9667             const SMDS_MeshElement* eComplex = invIt2->next();
9668             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9669             {
9670               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9671               if ( nbCommonNodes == e->NbNodes())
9672               {
9673                 complexFound = true;
9674                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9675                 break;
9676               }
9677             }
9678           }
9679         }
9680         if ( !complexFound )
9681           moreElemsToConvert.insert( e );
9682       }
9683     }
9684   }
9685   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9686   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9687 }
9688
9689 //=======================================================================
9690 //function : SewSideElements
9691 //purpose  :
9692 //=======================================================================
9693
9694 SMESH_MeshEditor::Sew_Error
9695 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9696                                    TIDSortedElemSet&    theSide2,
9697                                    const SMDS_MeshNode* theFirstNode1,
9698                                    const SMDS_MeshNode* theFirstNode2,
9699                                    const SMDS_MeshNode* theSecondNode1,
9700                                    const SMDS_MeshNode* theSecondNode2)
9701 {
9702   ClearLastCreated();
9703
9704   if ( theSide1.size() != theSide2.size() )
9705     return SEW_DIFF_NB_OF_ELEMENTS;
9706
9707   Sew_Error aResult = SEW_OK;
9708   // Algo:
9709   // 1. Build set of faces representing each side
9710   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9711   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9712
9713   // =======================================================================
9714   // 1. Build set of faces representing each side:
9715   // =======================================================================
9716   // a. build set of nodes belonging to faces
9717   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9718   // c. create temporary faces representing side of volumes if correspondent
9719   //    face does not exist
9720
9721   SMESHDS_Mesh* aMesh = GetMeshDS();
9722   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9723   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9724   TIDSortedElemSet             faceSet1, faceSet2;
9725   set<const SMDS_MeshElement*> volSet1,  volSet2;
9726   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9727   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9728   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9729   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9730   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9731   int iSide, iFace, iNode;
9732
9733   list<const SMDS_MeshElement* > tempFaceList;
9734   for ( iSide = 0; iSide < 2; iSide++ ) {
9735     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9736     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9737     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9738     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9739     set<const SMDS_MeshElement*>::iterator vIt;
9740     TIDSortedElemSet::iterator eIt;
9741     set<const SMDS_MeshNode*>::iterator    nIt;
9742
9743     // check that given nodes belong to given elements
9744     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9745     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9746     int firstIndex = -1, secondIndex = -1;
9747     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9748       const SMDS_MeshElement* elem = *eIt;
9749       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9750       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9751       if ( firstIndex > -1 && secondIndex > -1 ) break;
9752     }
9753     if ( firstIndex < 0 || secondIndex < 0 ) {
9754       // we can simply return until temporary faces created
9755       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9756     }
9757
9758     // -----------------------------------------------------------
9759     // 1a. Collect nodes of existing faces
9760     //     and build set of face nodes in order to detect missing
9761     //     faces corresponding to sides of volumes
9762     // -----------------------------------------------------------
9763
9764     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9765
9766     // loop on the given element of a side
9767     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9768       //const SMDS_MeshElement* elem = *eIt;
9769       const SMDS_MeshElement* elem = *eIt;
9770       if ( elem->GetType() == SMDSAbs_Face ) {
9771         faceSet->insert( elem );
9772         set <const SMDS_MeshNode*> faceNodeSet;
9773         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9774         while ( nodeIt->more() ) {
9775           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776           nodeSet->insert( n );
9777           faceNodeSet.insert( n );
9778         }
9779         setOfFaceNodeSet.insert( faceNodeSet );
9780       }
9781       else if ( elem->GetType() == SMDSAbs_Volume )
9782         volSet->insert( elem );
9783     }
9784     // ------------------------------------------------------------------------------
9785     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9786     // ------------------------------------------------------------------------------
9787
9788     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9789       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9790       while ( fIt->more() ) { // loop on faces sharing a node
9791         const SMDS_MeshElement* f = fIt->next();
9792         if ( faceSet->find( f ) == faceSet->end() ) {
9793           // check if all nodes are in nodeSet and
9794           // complete setOfFaceNodeSet if they are
9795           set <const SMDS_MeshNode*> faceNodeSet;
9796           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9797           bool allInSet = true;
9798           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9799             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9800             if ( nodeSet->find( n ) == nodeSet->end() )
9801               allInSet = false;
9802             else
9803               faceNodeSet.insert( n );
9804           }
9805           if ( allInSet ) {
9806             faceSet->insert( f );
9807             setOfFaceNodeSet.insert( faceNodeSet );
9808           }
9809         }
9810       }
9811     }
9812
9813     // -------------------------------------------------------------------------
9814     // 1c. Create temporary faces representing sides of volumes if correspondent
9815     //     face does not exist
9816     // -------------------------------------------------------------------------
9817
9818     if ( !volSet->empty() ) {
9819       //int nodeSetSize = nodeSet->size();
9820
9821       // loop on given volumes
9822       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9823         SMDS_VolumeTool vol (*vIt);
9824         // loop on volume faces: find free faces
9825         // --------------------------------------
9826         list<const SMDS_MeshElement* > freeFaceList;
9827         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9828           if ( !vol.IsFreeFace( iFace ))
9829             continue;
9830           // check if there is already a face with same nodes in a face set
9831           const SMDS_MeshElement* aFreeFace = 0;
9832           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9833           int nbNodes = vol.NbFaceNodes( iFace );
9834           set <const SMDS_MeshNode*> faceNodeSet;
9835           vol.GetFaceNodes( iFace, faceNodeSet );
9836           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9837           if ( isNewFace ) {
9838             // no such a face is given but it still can exist, check it
9839             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9840             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9841           }
9842           if ( !aFreeFace ) {
9843             // create a temporary face
9844             if ( nbNodes == 3 ) {
9845               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9846               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9847             }
9848             else if ( nbNodes == 4 ) {
9849               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9850               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9851             }
9852             else {
9853               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9854               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9855               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9856             }
9857             if ( aFreeFace )
9858               tempFaceList.push_back( aFreeFace );
9859           }
9860
9861           if ( aFreeFace )
9862             freeFaceList.push_back( aFreeFace );
9863
9864         } // loop on faces of a volume
9865
9866         // choose one of several free faces of a volume
9867         // --------------------------------------------
9868         if ( freeFaceList.size() > 1 ) {
9869           // choose a face having max nb of nodes shared by other elems of a side
9870           int maxNbNodes = -1;
9871           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9872           while ( fIt != freeFaceList.end() ) { // loop on free faces
9873             int nbSharedNodes = 0;
9874             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9875             while ( nodeIt->more() ) { // loop on free face nodes
9876               const SMDS_MeshNode* n =
9877                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9878               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9879               while ( invElemIt->more() ) {
9880                 const SMDS_MeshElement* e = invElemIt->next();
9881                 nbSharedNodes += faceSet->count( e );
9882                 nbSharedNodes += elemSet->count( e );
9883               }
9884             }
9885             if ( nbSharedNodes > maxNbNodes ) {
9886               maxNbNodes = nbSharedNodes;
9887               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9888             }
9889             else if ( nbSharedNodes == maxNbNodes ) {
9890               fIt++;
9891             }
9892             else {
9893               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9894             }
9895           }
9896           if ( freeFaceList.size() > 1 )
9897           {
9898             // could not choose one face, use another way
9899             // choose a face most close to the bary center of the opposite side
9900             gp_XYZ aBC( 0., 0., 0. );
9901             set <const SMDS_MeshNode*> addedNodes;
9902             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9903             eIt = elemSet2->begin();
9904             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9905               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9906               while ( nodeIt->more() ) { // loop on free face nodes
9907                 const SMDS_MeshNode* n =
9908                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909                 if ( addedNodes.insert( n ).second )
9910                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9911               }
9912             }
9913             aBC /= addedNodes.size();
9914             double minDist = DBL_MAX;
9915             fIt = freeFaceList.begin();
9916             while ( fIt != freeFaceList.end() ) { // loop on free faces
9917               double dist = 0;
9918               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9919               while ( nodeIt->more() ) { // loop on free face nodes
9920                 const SMDS_MeshNode* n =
9921                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9923                 dist += ( aBC - p ).SquareModulus();
9924               }
9925               if ( dist < minDist ) {
9926                 minDist = dist;
9927                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9928               }
9929               else
9930                 fIt = freeFaceList.erase( fIt++ );
9931             }
9932           }
9933         } // choose one of several free faces of a volume
9934
9935         if ( freeFaceList.size() == 1 ) {
9936           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9937           faceSet->insert( aFreeFace );
9938           // complete a node set with nodes of a found free face
9939           //           for ( iNode = 0; iNode < ; iNode++ )
9940           //             nodeSet->insert( fNodes[ iNode ] );
9941         }
9942
9943       } // loop on volumes of a side
9944
9945       //       // complete a set of faces if new nodes in a nodeSet appeared
9946       //       // ----------------------------------------------------------
9947       //       if ( nodeSetSize != nodeSet->size() ) {
9948       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9949       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9950       //           while ( fIt->more() ) { // loop on faces sharing a node
9951       //             const SMDS_MeshElement* f = fIt->next();
9952       //             if ( faceSet->find( f ) == faceSet->end() ) {
9953       //               // check if all nodes are in nodeSet and
9954       //               // complete setOfFaceNodeSet if they are
9955       //               set <const SMDS_MeshNode*> faceNodeSet;
9956       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9957       //               bool allInSet = true;
9958       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9959       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9961       //                   allInSet = false;
9962       //                 else
9963       //                   faceNodeSet.insert( n );
9964       //               }
9965       //               if ( allInSet ) {
9966       //                 faceSet->insert( f );
9967       //                 setOfFaceNodeSet.insert( faceNodeSet );
9968       //               }
9969       //             }
9970       //           }
9971       //         }
9972       //       }
9973     } // Create temporary faces, if there are volumes given
9974   } // loop on sides
9975
9976   if ( faceSet1.size() != faceSet2.size() ) {
9977     // delete temporary faces: they are in reverseElements of actual nodes
9978     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9979     //    while ( tmpFaceIt->more() )
9980     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9981     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9982     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9983     //      aMesh->RemoveElement(*tmpFaceIt);
9984     MESSAGE("Diff nb of faces");
9985     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9986   }
9987
9988   // ============================================================
9989   // 2. Find nodes to merge:
9990   //              bind a node to remove to a node to put instead
9991   // ============================================================
9992
9993   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9994   if ( theFirstNode1 != theFirstNode2 )
9995     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9996   if ( theSecondNode1 != theSecondNode2 )
9997     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9998
9999   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10000   set< long > linkIdSet; // links to process
10001   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10002
10003   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10004   list< NLink > linkList[2];
10005   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10006   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10007   // loop on links in linkList; find faces by links and append links
10008   // of the found faces to linkList
10009   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10010   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10011   {
10012     NLink link[] = { *linkIt[0], *linkIt[1] };
10013     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10014     if ( !linkIdSet.count( linkID ) )
10015       continue;
10016
10017     // by links, find faces in the face sets,
10018     // and find indices of link nodes in the found faces;
10019     // in a face set, there is only one or no face sharing a link
10020     // ---------------------------------------------------------------
10021
10022     const SMDS_MeshElement* face[] = { 0, 0 };
10023     vector<const SMDS_MeshNode*> fnodes[2];
10024     int iLinkNode[2][2];
10025     TIDSortedElemSet avoidSet;
10026     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10027       const SMDS_MeshNode* n1 = link[iSide].first;
10028       const SMDS_MeshNode* n2 = link[iSide].second;
10029       //cout << "Side " << iSide << " ";
10030       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10031       // find a face by two link nodes
10032       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10033                                                       *faceSetPtr[ iSide ], avoidSet,
10034                                                       &iLinkNode[iSide][0],
10035                                                       &iLinkNode[iSide][1] );
10036       if ( face[ iSide ])
10037       {
10038         //cout << " F " << face[ iSide]->GetID() <<endl;
10039         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10040         // put face nodes to fnodes
10041         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10042         fnodes[ iSide ].assign( nIt, nEnd );
10043         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10044       }
10045     }
10046
10047     // check similarity of elements of the sides
10048     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10049       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10050       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10051         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10052       }
10053       else {
10054         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10055       }
10056       break; // do not return because it's necessary to remove tmp faces
10057     }
10058
10059     // set nodes to merge
10060     // -------------------
10061
10062     if ( face[0] && face[1] )  {
10063       const int nbNodes = face[0]->NbNodes();
10064       if ( nbNodes != face[1]->NbNodes() ) {
10065         MESSAGE("Diff nb of face nodes");
10066         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10067         break; // do not return because it s necessary to remove tmp faces
10068       }
10069       bool reverse[] = { false, false }; // order of nodes in the link
10070       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10071         // analyse link orientation in faces
10072         int i1 = iLinkNode[ iSide ][ 0 ];
10073         int i2 = iLinkNode[ iSide ][ 1 ];
10074         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10075       }
10076       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10077       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10078       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10079       {
10080         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10081                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10082       }
10083
10084       // add other links of the faces to linkList
10085       // -----------------------------------------
10086
10087       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10088         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10089         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10090         if ( !iter_isnew.second ) { // already in a set: no need to process
10091           linkIdSet.erase( iter_isnew.first );
10092         }
10093         else // new in set == encountered for the first time: add
10094         {
10095           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10096           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10097           linkList[0].push_back ( NLink( n1, n2 ));
10098           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10099         }
10100       }
10101     } // 2 faces found
10102
10103     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10104       break;
10105
10106   } // loop on link lists
10107
10108   if ( aResult == SEW_OK &&
10109        ( //linkIt[0] != linkList[0].end() ||
10110         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10111     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10112              " " << (faceSetPtr[1]->empty()));
10113     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10114   }
10115
10116   // ====================================================================
10117   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10118   // ====================================================================
10119
10120   // delete temporary faces
10121   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10122   //  while ( tmpFaceIt->more() )
10123   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10124   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10125   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10126     aMesh->RemoveElement(*tmpFaceIt);
10127
10128   if ( aResult != SEW_OK)
10129     return aResult;
10130
10131   list< int > nodeIDsToRemove;
10132   vector< const SMDS_MeshNode*> nodes;
10133   ElemFeatures elemType;
10134
10135   // loop on nodes replacement map
10136   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10137   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10138     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10139     {
10140       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10141       nodeIDsToRemove.push_back( nToRemove->GetID() );
10142       // loop on elements sharing nToRemove
10143       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10144       while ( invElemIt->more() ) {
10145         const SMDS_MeshElement* e = invElemIt->next();
10146         // get a new suite of nodes: make replacement
10147         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10148         nodes.resize( nbNodes );
10149         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10150         while ( nIt->more() ) {
10151           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10152           nnIt = nReplaceMap.find( n );
10153           if ( nnIt != nReplaceMap.end() ) {
10154             nbReplaced++;
10155             n = (*nnIt).second;
10156           }
10157           nodes[ i++ ] = n;
10158         }
10159         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10160         //         elemIDsToRemove.push_back( e->GetID() );
10161         //       else
10162         if ( nbReplaced )
10163         {
10164           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10165           aMesh->RemoveElement( e );
10166
10167           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10168           {
10169             AddToSameGroups( newElem, e, aMesh );
10170             if ( int aShapeId = e->getshapeId() )
10171               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10172           }
10173         }
10174       }
10175     }
10176
10177   Remove( nodeIDsToRemove, true );
10178
10179   return aResult;
10180 }
10181
10182 //================================================================================
10183 /*!
10184  * \brief Find corresponding nodes in two sets of faces
10185  * \param theSide1 - first face set
10186  * \param theSide2 - second first face
10187  * \param theFirstNode1 - a boundary node of set 1
10188  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10189  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10190  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10191  * \param nReplaceMap - output map of corresponding nodes
10192  * \return bool  - is a success or not
10193  */
10194 //================================================================================
10195
10196 #ifdef _DEBUG_
10197 //#define DEBUG_MATCHING_NODES
10198 #endif
10199
10200 SMESH_MeshEditor::Sew_Error
10201 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10202                                     set<const SMDS_MeshElement*>& theSide2,
10203                                     const SMDS_MeshNode*          theFirstNode1,
10204                                     const SMDS_MeshNode*          theFirstNode2,
10205                                     const SMDS_MeshNode*          theSecondNode1,
10206                                     const SMDS_MeshNode*          theSecondNode2,
10207                                     TNodeNodeMap &                nReplaceMap)
10208 {
10209   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10210
10211   nReplaceMap.clear();
10212   if ( theFirstNode1 != theFirstNode2 )
10213     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10214   if ( theSecondNode1 != theSecondNode2 )
10215     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10216
10217   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10218   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10219
10220   list< NLink > linkList[2];
10221   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10222   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10223
10224   // loop on links in linkList; find faces by links and append links
10225   // of the found faces to linkList
10226   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10228     NLink link[] = { *linkIt[0], *linkIt[1] };
10229     if ( linkSet.find( link[0] ) == linkSet.end() )
10230       continue;
10231
10232     // by links, find faces in the face sets,
10233     // and find indices of link nodes in the found faces;
10234     // in a face set, there is only one or no face sharing a link
10235     // ---------------------------------------------------------------
10236
10237     const SMDS_MeshElement* face[] = { 0, 0 };
10238     list<const SMDS_MeshNode*> notLinkNodes[2];
10239     //bool reverse[] = { false, false }; // order of notLinkNodes
10240     int nbNodes[2];
10241     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10242     {
10243       const SMDS_MeshNode* n1 = link[iSide].first;
10244       const SMDS_MeshNode* n2 = link[iSide].second;
10245       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10246       set< const SMDS_MeshElement* > facesOfNode1;
10247       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10248       {
10249         // during a loop of the first node, we find all faces around n1,
10250         // during a loop of the second node, we find one face sharing both n1 and n2
10251         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10252         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10253         while ( fIt->more() ) { // loop on faces sharing a node
10254           const SMDS_MeshElement* f = fIt->next();
10255           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10256               ! facesOfNode1.insert( f ).second ) // f encounters twice
10257           {
10258             if ( face[ iSide ] ) {
10259               MESSAGE( "2 faces per link " );
10260               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10261             }
10262             face[ iSide ] = f;
10263             faceSet->erase( f );
10264
10265             // get not link nodes
10266             int nbN = f->NbNodes();
10267             if ( f->IsQuadratic() )
10268               nbN /= 2;
10269             nbNodes[ iSide ] = nbN;
10270             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10271             int i1 = f->GetNodeIndex( n1 );
10272             int i2 = f->GetNodeIndex( n2 );
10273             int iEnd = nbN, iBeg = -1, iDelta = 1;
10274             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10275             if ( reverse ) {
10276               std::swap( iEnd, iBeg ); iDelta = -1;
10277             }
10278             int i = i2;
10279             while ( true ) {
10280               i += iDelta;
10281               if ( i == iEnd ) i = iBeg + iDelta;
10282               if ( i == i1 ) break;
10283               nodes.push_back ( f->GetNode( i ) );
10284             }
10285           }
10286         }
10287       }
10288     }
10289     // check similarity of elements of the sides
10290     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10291       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10292       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10293         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10294       }
10295       else {
10296         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10297       }
10298     }
10299
10300     // set nodes to merge
10301     // -------------------
10302
10303     if ( face[0] && face[1] )  {
10304       if ( nbNodes[0] != nbNodes[1] ) {
10305         MESSAGE("Diff nb of face nodes");
10306         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307       }
10308 #ifdef DEBUG_MATCHING_NODES
10309       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10310                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10311                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10312 #endif
10313       int nbN = nbNodes[0];
10314       {
10315         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10316         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10317         for ( int i = 0 ; i < nbN - 2; ++i ) {
10318 #ifdef DEBUG_MATCHING_NODES
10319           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10320 #endif
10321           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10322         }
10323       }
10324
10325       // add other links of the face 1 to linkList
10326       // -----------------------------------------
10327
10328       const SMDS_MeshElement* f0 = face[0];
10329       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10330       for ( int i = 0; i < nbN; i++ )
10331       {
10332         const SMDS_MeshNode* n2 = f0->GetNode( i );
10333         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10334           linkSet.insert( SMESH_TLink( n1, n2 ));
10335         if ( !iter_isnew.second ) { // already in a set: no need to process
10336           linkSet.erase( iter_isnew.first );
10337         }
10338         else // new in set == encountered for the first time: add
10339         {
10340 #ifdef DEBUG_MATCHING_NODES
10341           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10342                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10343 #endif
10344           linkList[0].push_back ( NLink( n1, n2 ));
10345           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10346         }
10347         n1 = n2;
10348       }
10349     } // 2 faces found
10350   } // loop on link lists
10351
10352   return SEW_OK;
10353 }
10354
10355 namespace // automatically find theAffectedElems for DoubleNodes()
10356 {
10357   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10358
10359   //--------------------------------------------------------------------------------
10360   // Nodes shared by adjacent FissureBorder's.
10361   // 1 node  if FissureBorder separates faces
10362   // 2 nodes if FissureBorder separates volumes
10363   struct SubBorder
10364   {
10365     const SMDS_MeshNode* _nodes[2];
10366     int                  _nbNodes;
10367
10368     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10369     {
10370       _nodes[0] = n1;
10371       _nodes[1] = n2;
10372       _nbNodes = bool( n1 ) + bool( n2 );
10373       if ( _nbNodes == 2 && n1 > n2 )
10374         std::swap( _nodes[0], _nodes[1] );
10375     }
10376     bool operator<( const SubBorder& other ) const
10377     {
10378       for ( int i = 0; i < _nbNodes; ++i )
10379       {
10380         if ( _nodes[i] < other._nodes[i] ) return true;
10381         if ( _nodes[i] > other._nodes[i] ) return false;
10382       }
10383       return false;
10384     }
10385   };
10386
10387   //--------------------------------------------------------------------------------
10388   // Map a SubBorder to all FissureBorder it bounds
10389   struct FissureBorder;
10390   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10391   typedef TBorderLinks::iterator                               TMappedSub;
10392
10393   //--------------------------------------------------------------------------------
10394   /*!
10395    * \brief Element border (volume facet or face edge) at a fissure
10396    */
10397   struct FissureBorder
10398   {
10399     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10400     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10401
10402     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10403     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10404
10405     FissureBorder( FissureBorder && from ) // move constructor
10406     {
10407       std::swap( _nodes,       from._nodes );
10408       std::swap( _sortedNodes, from._sortedNodes );
10409       _elems[0] = from._elems[0];
10410       _elems[1] = from._elems[1];
10411     }
10412
10413     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10414                    std::vector< const SMDS_MeshElement* > & adjElems)
10415       : _nodes( elemToDuplicate->NbCornerNodes() )
10416     {
10417       for ( size_t i = 0; i < _nodes.size(); ++i )
10418         _nodes[i] = elemToDuplicate->GetNode( i );
10419
10420       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10421       findAdjacent( type, adjElems );
10422     }
10423
10424     FissureBorder( const SMDS_MeshNode**                    nodes,
10425                    const size_t                             nbNodes,
10426                    const SMDSAbs_ElementType                adjElemsType,
10427                    std::vector< const SMDS_MeshElement* > & adjElems)
10428       : _nodes( nodes, nodes + nbNodes )
10429     {
10430       findAdjacent( adjElemsType, adjElems );
10431     }
10432
10433     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10434                        std::vector< const SMDS_MeshElement* > & adjElems)
10435     {
10436       _elems[0] = _elems[1] = 0;
10437       adjElems.clear();
10438       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10439         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10440           _elems[i] = adjElems[i];
10441     }
10442
10443     bool operator<( const FissureBorder& other ) const
10444     {
10445       return GetSortedNodes() < other.GetSortedNodes();
10446     }
10447
10448     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10449     {
10450       if ( _sortedNodes.empty() && !_nodes.empty() )
10451       {
10452         FissureBorder* me = const_cast<FissureBorder*>( this );
10453         me->_sortedNodes = me->_nodes;
10454         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10455       }
10456       return _sortedNodes;
10457     }
10458
10459     size_t NbSub() const
10460     {
10461       return _nodes.size();
10462     }
10463
10464     SubBorder Sub(size_t i) const
10465     {
10466       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10467     }
10468
10469     void AddSelfTo( TBorderLinks& borderLinks )
10470     {
10471       _mappedSubs.resize( NbSub() );
10472       for ( size_t i = 0; i < NbSub(); ++i )
10473       {
10474         TBorderLinks::iterator s2b =
10475           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10476         s2b->second.push_back( this );
10477         _mappedSubs[ i ] = s2b;
10478       }
10479     }
10480
10481     void Clear()
10482     {
10483       _nodes.clear();
10484     }
10485
10486     const SMDS_MeshElement* GetMarkedElem() const
10487     {
10488       if ( _nodes.empty() ) return 0; // cleared
10489       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10490       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10491       return 0;
10492     }
10493
10494     gp_XYZ GetNorm() const // normal to the border
10495     {
10496       gp_XYZ norm;
10497       if ( _nodes.size() == 2 )
10498       {
10499         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10500         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10501           avgNorm += norm;
10502         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10503           avgNorm += norm;
10504
10505         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10506         norm = bordDir ^ avgNorm;
10507       }
10508       else
10509       {
10510         SMESH_NodeXYZ p0( _nodes[0] );
10511         SMESH_NodeXYZ p1( _nodes[1] );
10512         SMESH_NodeXYZ p2( _nodes[2] );
10513         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10514       }
10515       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10516         norm.Reverse();
10517
10518       return norm;
10519     }
10520
10521     void ChooseSide() // mark an _elem located at positive side of fissure
10522     {
10523       _elems[0]->setIsMarked( true );
10524       gp_XYZ norm = GetNorm();
10525       double maxX = norm.Coord(1);
10526       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10527       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10528       if ( maxX < 0 )
10529       {
10530         _elems[0]->setIsMarked( false );
10531         _elems[1]->setIsMarked( true );
10532       }
10533     }
10534
10535   }; // struct FissureBorder
10536
10537   //--------------------------------------------------------------------------------
10538   /*!
10539    * \brief Classifier of elements at fissure edge
10540    */
10541   class FissureNormal
10542   {
10543     std::vector< gp_XYZ > _normals;
10544     bool                  _bothIn;
10545
10546   public:
10547     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10548     {
10549       _bothIn = false;
10550       _normals.reserve(2);
10551       _normals.push_back( bord.GetNorm() );
10552       if ( _normals.size() == 2 )
10553         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10554     }
10555
10556     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10557     {
10558       bool isIn = false;
10559       switch ( _normals.size() ) {
10560       case 1:
10561       {
10562         isIn = !isOut( n, _normals[0], elem );
10563         break;
10564       }
10565       case 2:
10566       {
10567         bool in1 = !isOut( n, _normals[0], elem );
10568         bool in2 = !isOut( n, _normals[1], elem );
10569         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10570       }
10571       }
10572       return isIn;
10573     }
10574   };
10575
10576   //================================================================================
10577   /*!
10578    * \brief Classify an element by a plane passing through a node
10579    */
10580   //================================================================================
10581
10582   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10583   {
10584     SMESH_NodeXYZ p = n;
10585     double sumDot = 0;
10586     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10587     {
10588       SMESH_NodeXYZ pi = elem->GetNode( i );
10589       sumDot += norm * ( pi - p );
10590     }
10591     return sumDot < -1e-100;
10592   }
10593
10594   //================================================================================
10595   /*!
10596    * \brief Find FissureBorder's by nodes to duplicate
10597    */
10598   //================================================================================
10599
10600   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10601                            std::vector< FissureBorder > & theFissureBorders )
10602   {
10603     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10604     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10605     if ( !n ) return;
10606     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10607     if ( n->NbInverseElements( elemType ) == 0 )
10608     {
10609       elemType = SMDSAbs_Face;
10610       if ( n->NbInverseElements( elemType ) == 0 )
10611         return;
10612     }
10613     // unmark elements touching the fissure
10614     for ( ; nIt != theNodes.end(); ++nIt )
10615       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10616
10617     // loop on elements touching the fissure to get their borders belonging to the fissure
10618     std::set< FissureBorder >              fissureBorders;
10619     std::vector< const SMDS_MeshElement* > adjElems;
10620     std::vector< const SMDS_MeshNode* >    nodes;
10621     SMDS_VolumeTool volTool;
10622     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10623     {
10624       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10625       while ( invIt->more() )
10626       {
10627         const SMDS_MeshElement* eInv = invIt->next();
10628         if ( eInv->isMarked() ) continue;
10629         eInv->setIsMarked( true );
10630
10631         if ( elemType == SMDSAbs_Volume )
10632         {
10633           volTool.Set( eInv );
10634           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10635           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10636           {
10637             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10638             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10639             nodes.clear();
10640             bool allOnFissure = true;
10641             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10642               if (( allOnFissure = theNodes.count( nn[ iN ])))
10643                 nodes.push_back( nn[ iN ]);
10644             if ( allOnFissure )
10645               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10646                                                                elemType, adjElems )));
10647           }
10648         }
10649         else // elemType == SMDSAbs_Face
10650         {
10651           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10652           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10653           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10654           {
10655             nn[1]      = eInv->GetNode( iN );
10656             onFissure1 = theNodes.count( nn[1] );
10657             if ( onFissure0 && onFissure1 )
10658               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10659             nn[0]      = nn[1];
10660             onFissure0 = onFissure1;
10661           }
10662         }
10663       }
10664     }
10665
10666     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10667     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10668     for ( ; bord != fissureBorders.end(); ++bord )
10669     {
10670       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10671     }
10672     return;
10673   } // findFissureBorders()
10674
10675   //================================================================================
10676   /*!
10677    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10678    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10679    *  \param [in] theNodesNot - nodes not to duplicate
10680    *  \param [out] theAffectedElems - the found elements
10681    */
10682   //================================================================================
10683
10684   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10685                           TIDSortedElemSet&       theAffectedElems)
10686   {
10687     if ( theElemsOrNodes.empty() ) return;
10688
10689     // find FissureBorder's
10690
10691     std::vector< FissureBorder >           fissure;
10692     std::vector< const SMDS_MeshElement* > elemsByFacet;
10693
10694     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10695     if ( (*elIt)->GetType() == SMDSAbs_Node )
10696     {
10697       findFissureBorders( theElemsOrNodes, fissure );
10698     }
10699     else
10700     {
10701       fissure.reserve( theElemsOrNodes.size() );
10702       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10703         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10704     }
10705     if ( fissure.empty() )
10706       return;
10707
10708     // fill borderLinks
10709
10710     TBorderLinks borderLinks;
10711
10712     for ( size_t i = 0; i < fissure.size(); ++i )
10713     {
10714       fissure[i].AddSelfTo( borderLinks );
10715     }
10716
10717     // get theAffectedElems
10718
10719     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10720     for ( size_t i = 0; i < fissure.size(); ++i )
10721       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10722       {
10723         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10724                                         false, /*markElem=*/true );
10725       }
10726
10727     std::vector<const SMDS_MeshNode *>                 facetNodes;
10728     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10729     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10730
10731     // choose a side of fissure
10732     fissure[0].ChooseSide();
10733     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10734
10735     size_t nbCheckedBorders = 0;
10736     while ( nbCheckedBorders < fissure.size() )
10737     {
10738       // find a FissureBorder to treat
10739       FissureBorder* bord = 0;
10740       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10741         if ( fissure[i].GetMarkedElem() )
10742           bord = & fissure[i];
10743       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10744         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10745         {
10746           bord = & fissure[i];
10747           bord->ChooseSide();
10748           theAffectedElems.insert( bord->GetMarkedElem() );
10749         }
10750       if ( !bord ) return;
10751       ++nbCheckedBorders;
10752
10753       // treat FissureBorder's linked to bord
10754       fissureNodes.clear();
10755       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10756       for ( size_t i = 0; i < bord->NbSub(); ++i )
10757       {
10758         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10759         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10760         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10761         const SubBorder&                          sb = l2b->first;
10762         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10763
10764         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10765         {
10766           for ( int j = 0; j < sb._nbNodes; ++j )
10767             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10768           continue;
10769         }
10770
10771         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10772         // until an elem adjacent to a neighbour FissureBorder is found
10773         facetNodes.clear();
10774         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10775         facetNodes.resize( sb._nbNodes + 1 );
10776
10777         while ( bordElem )
10778         {
10779           // check if bordElem is adjacent to a neighbour FissureBorder
10780           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10781           {
10782             FissureBorder* bord2 = linkedBorders[j];
10783             if ( bord2 == bord ) continue;
10784             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10785               bordElem = 0;
10786             else
10787               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10788           }
10789           if ( !bordElem )
10790             break;
10791
10792           // find the next bordElem
10793           const SMDS_MeshElement* nextBordElem = 0;
10794           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10795           {
10796             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10797             if ( fissureNodes.count( n )) continue;
10798
10799             facetNodes[ sb._nbNodes ] = n;
10800             elemsByFacet.clear();
10801             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10802             {
10803               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10804                 if ( elemsByFacet[ iE ] != bordElem &&
10805                      !elemsByFacet[ iE ]->isMarked() )
10806                 {
10807                   theAffectedElems.insert( elemsByFacet[ iE ]);
10808                   elemsByFacet[ iE ]->setIsMarked( true );
10809                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10810                     nextBordElem = elemsByFacet[ iE ];
10811                 }
10812             }
10813           }
10814           bordElem = nextBordElem;
10815
10816         } // while ( bordElem )
10817
10818         linkedBorders.clear(); // not to treat this link any more
10819
10820       } // loop on SubBorder's of a FissureBorder
10821
10822       bord->Clear();
10823
10824     } // loop on FissureBorder's
10825
10826
10827     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10828
10829     // mark nodes of theAffectedElems
10830     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10831
10832     // unmark nodes of the fissure
10833     elIt = theElemsOrNodes.begin();
10834     if ( (*elIt)->GetType() == SMDSAbs_Node )
10835       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10836     else
10837       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10838
10839     std::vector< gp_XYZ > normVec;
10840
10841     // loop on nodes of the fissure, add elements having marked nodes
10842     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10843     {
10844       const SMDS_MeshElement* e = (*elIt);
10845       if ( e->GetType() != SMDSAbs_Node )
10846         e->setIsMarked( true ); // avoid adding a fissure element
10847
10848       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10849       {
10850         const SMDS_MeshNode* n = e->GetNode( iN );
10851         if ( fissEdgeNodes2Norm.count( n ))
10852           continue;
10853
10854         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10855         while ( invIt->more() )
10856         {
10857           const SMDS_MeshElement* eInv = invIt->next();
10858           if ( eInv->isMarked() ) continue;
10859           eInv->setIsMarked( true );
10860
10861           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10862           while( nIt->more() )
10863             if ( nIt->next()->isMarked())
10864             {
10865               theAffectedElems.insert( eInv );
10866               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10867               n->setIsMarked( false );
10868               break;
10869             }
10870         }
10871       }
10872     }
10873
10874     // add elements on the fissure edge
10875     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10876     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10877     {
10878       const SMDS_MeshNode* edgeNode = n2N->first;
10879       const FissureNormal & normals = n2N->second;
10880
10881       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10882       while ( invIt->more() )
10883       {
10884         const SMDS_MeshElement* eInv = invIt->next();
10885         if ( eInv->isMarked() ) continue;
10886         eInv->setIsMarked( true );
10887
10888         // classify eInv using normals
10889         bool toAdd = normals.IsIn( edgeNode, eInv );
10890         if ( toAdd ) // check if all nodes lie on the fissure edge
10891         {
10892           bool notOnEdge = false;
10893           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10894             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10895           toAdd = notOnEdge;
10896         }
10897         if ( toAdd )
10898         {
10899           theAffectedElems.insert( eInv );
10900         }
10901       }
10902     }
10903
10904     return;
10905   } // findAffectedElems()
10906 } // namespace
10907
10908 //================================================================================
10909 /*!
10910  * \brief Create elements equal (on same nodes) to given ones
10911  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10912  *              elements of the uppest dimension are duplicated.
10913  */
10914 //================================================================================
10915
10916 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10917 {
10918   ClearLastCreated();
10919   SMESHDS_Mesh* mesh = GetMeshDS();
10920
10921   // get an element type and an iterator over elements
10922
10923   SMDSAbs_ElementType type = SMDSAbs_All;
10924   SMDS_ElemIteratorPtr elemIt;
10925   if ( theElements.empty() )
10926   {
10927     if ( mesh->NbNodes() == 0 )
10928       return;
10929     // get most complex type
10930     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10931       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10932       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10933     };
10934     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10935       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10936       {
10937         type = types[i];
10938         elemIt = mesh->elementsIterator( type );
10939         break;
10940       }
10941   }
10942   else
10943   {
10944     type = (*theElements.begin())->GetType();
10945     elemIt = SMESHUtils::elemSetIterator( theElements );
10946   }
10947
10948   // un-mark all elements to avoid duplicating just created elements
10949   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10950
10951   // duplicate elements
10952
10953   ElemFeatures elemType;
10954
10955   vector< const SMDS_MeshNode* > nodes;
10956   while ( elemIt->more() )
10957   {
10958     const SMDS_MeshElement* elem = elemIt->next();
10959     if ( elem->GetType() != type || elem->isMarked() )
10960       continue;
10961
10962     elemType.Init( elem, /*basicOnly=*/false );
10963     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10964
10965     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10966       newElem->setIsMarked( true );
10967   }
10968 }
10969
10970 //================================================================================
10971 /*!
10972   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10973   \param theElems - the list of elements (edges or faces) to be replicated
10974   The nodes for duplication could be found from these elements
10975   \param theNodesNot - list of nodes to NOT replicate
10976   \param theAffectedElems - the list of elements (cells and edges) to which the
10977   replicated nodes should be associated to.
10978   \return TRUE if operation has been completed successfully, FALSE otherwise
10979 */
10980 //================================================================================
10981
10982 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10983                                     const TIDSortedElemSet& theNodesNot,
10984                                     const TIDSortedElemSet& theAffectedElems )
10985 {
10986   ClearLastCreated();
10987
10988   if ( theElems.size() == 0 )
10989     return false;
10990
10991   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10992   if ( !aMeshDS )
10993     return false;
10994
10995   bool res = false;
10996   TNodeNodeMap anOldNodeToNewNode;
10997   // duplicate elements and nodes
10998   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10999   // replce nodes by duplications
11000   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11001   return res;
11002 }
11003
11004 //================================================================================
11005 /*!
11006   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11007   \param theMeshDS - mesh instance
11008   \param theElems - the elements replicated or modified (nodes should be changed)
11009   \param theNodesNot - nodes to NOT replicate
11010   \param theNodeNodeMap - relation of old node to new created node
11011   \param theIsDoubleElem - flag os to replicate element or modify
11012   \return TRUE if operation has been completed successfully, FALSE otherwise
11013 */
11014 //================================================================================
11015
11016 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11017                                    const TIDSortedElemSet& theElems,
11018                                    const TIDSortedElemSet& theNodesNot,
11019                                    TNodeNodeMap&           theNodeNodeMap,
11020                                    const bool              theIsDoubleElem )
11021 {
11022   // iterate through element and duplicate them (by nodes duplication)
11023   bool res = false;
11024   std::vector<const SMDS_MeshNode*> newNodes;
11025   ElemFeatures elemType;
11026
11027   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11028   for ( ;  elemItr != theElems.end(); ++elemItr )
11029   {
11030     const SMDS_MeshElement* anElem = *elemItr;
11031     // if (!anElem)
11032     //   continue;
11033
11034     // duplicate nodes to duplicate element
11035     bool isDuplicate = false;
11036     newNodes.resize( anElem->NbNodes() );
11037     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11038     int ind = 0;
11039     while ( anIter->more() )
11040     {
11041       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11042       const SMDS_MeshNode*  aNewNode = aCurrNode;
11043       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11044       if ( n2n != theNodeNodeMap.end() )
11045       {
11046         aNewNode = n2n->second;
11047       }
11048       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11049       {
11050         // duplicate node
11051         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11052         copyPosition( aCurrNode, aNewNode );
11053         theNodeNodeMap[ aCurrNode ] = aNewNode;
11054         myLastCreatedNodes.push_back( aNewNode );
11055       }
11056       isDuplicate |= (aCurrNode != aNewNode);
11057       newNodes[ ind++ ] = aNewNode;
11058     }
11059     if ( !isDuplicate )
11060       continue;
11061
11062     if ( theIsDoubleElem )
11063       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11064     else
11065       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11066
11067     res = true;
11068   }
11069   return res;
11070 }
11071
11072 //================================================================================
11073 /*!
11074   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11075   \param theNodes - identifiers of nodes to be doubled
11076   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11077   nodes. If list of element identifiers is empty then nodes are doubled but
11078   they not assigned to elements
11079   \return TRUE if operation has been completed successfully, FALSE otherwise
11080 */
11081 //================================================================================
11082
11083 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11084                                     const std::list< int >& theListOfModifiedElems )
11085 {
11086   ClearLastCreated();
11087
11088   if ( theListOfNodes.size() == 0 )
11089     return false;
11090
11091   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11092   if ( !aMeshDS )
11093     return false;
11094
11095   // iterate through nodes and duplicate them
11096
11097   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11098
11099   std::list< int >::const_iterator aNodeIter;
11100   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11101   {
11102     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11103     if ( !aNode )
11104       continue;
11105
11106     // duplicate node
11107
11108     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11109     if ( aNewNode )
11110     {
11111       copyPosition( aNode, aNewNode );
11112       anOldNodeToNewNode[ aNode ] = aNewNode;
11113       myLastCreatedNodes.push_back( aNewNode );
11114     }
11115   }
11116
11117   // Change nodes of elements
11118
11119   std::vector<const SMDS_MeshNode*> aNodeArr;
11120
11121   std::list< int >::const_iterator anElemIter;
11122   for ( anElemIter =  theListOfModifiedElems.begin();
11123         anElemIter != theListOfModifiedElems.end();
11124         anElemIter++ )
11125   {
11126     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11127     if ( !anElem )
11128       continue;
11129
11130     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11131     for( size_t i = 0; i < aNodeArr.size(); ++i )
11132     {
11133       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11134         anOldNodeToNewNode.find( aNodeArr[ i ]);
11135       if ( n2n != anOldNodeToNewNode.end() )
11136         aNodeArr[ i ] = n2n->second;
11137     }
11138     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11139   }
11140
11141   return true;
11142 }
11143
11144 namespace {
11145
11146   //================================================================================
11147   /*!
11148     \brief Check if element located inside shape
11149     \return TRUE if IN or ON shape, FALSE otherwise
11150   */
11151   //================================================================================
11152
11153   template<class Classifier>
11154   bool isInside(const SMDS_MeshElement* theElem,
11155                 Classifier&             theClassifier,
11156                 const double            theTol)
11157   {
11158     gp_XYZ centerXYZ (0, 0, 0);
11159     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11160       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11161
11162     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11163     theClassifier.Perform(aPnt, theTol);
11164     TopAbs_State aState = theClassifier.State();
11165     return (aState == TopAbs_IN || aState == TopAbs_ON );
11166   }
11167
11168   //================================================================================
11169   /*!
11170    * \brief Classifier of the 3D point on the TopoDS_Face
11171    *        with interaface suitable for isInside()
11172    */
11173   //================================================================================
11174
11175   struct _FaceClassifier
11176   {
11177     Extrema_ExtPS       _extremum;
11178     BRepAdaptor_Surface _surface;
11179     TopAbs_State        _state;
11180
11181     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11182     {
11183       _extremum.Initialize( _surface,
11184                             _surface.FirstUParameter(), _surface.LastUParameter(),
11185                             _surface.FirstVParameter(), _surface.LastVParameter(),
11186                             _surface.Tolerance(), _surface.Tolerance() );
11187     }
11188     void Perform(const gp_Pnt& aPnt, double theTol)
11189     {
11190       theTol *= theTol;
11191       _state = TopAbs_OUT;
11192       _extremum.Perform(aPnt);
11193       if ( _extremum.IsDone() )
11194         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11195           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11196     }
11197     TopAbs_State State() const
11198     {
11199       return _state;
11200     }
11201   };
11202 }
11203
11204 //================================================================================
11205 /*!
11206   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11207   This method is the first step of DoubleNodeElemGroupsInRegion.
11208   \param theElems - list of groups of elements (edges or faces) to be replicated
11209   \param theNodesNot - list of groups of nodes not to replicated
11210   \param theShape - shape to detect affected elements (element which geometric center
11211          located on or inside shape). If the shape is null, detection is done on faces orientations
11212          (select elements with a gravity center on the side given by faces normals).
11213          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11214          The replicated nodes should be associated to affected elements.
11215   \return true
11216   \sa DoubleNodeElemGroupsInRegion()
11217 */
11218 //================================================================================
11219
11220 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11221                                                    const TIDSortedElemSet& theNodesNot,
11222                                                    const TopoDS_Shape&     theShape,
11223                                                    TIDSortedElemSet&       theAffectedElems)
11224 {
11225   if ( theShape.IsNull() )
11226   {
11227     findAffectedElems( theElems, theAffectedElems );
11228   }
11229   else
11230   {
11231     const double aTol = Precision::Confusion();
11232     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11233     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
11234     if ( theShape.ShapeType() == TopAbs_SOLID )
11235     {
11236       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11237       bsc3d->PerformInfinitePoint(aTol);
11238     }
11239     else if (theShape.ShapeType() == TopAbs_FACE )
11240     {
11241       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11242     }
11243
11244     // iterates on indicated elements and get elements by back references from their nodes
11245     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11246     for ( ;  elemItr != theElems.end(); ++elemItr )
11247     {
11248       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11249       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11250       while ( nodeItr->more() )
11251       {
11252         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11253         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11254           continue;
11255         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11256         while ( backElemItr->more() )
11257         {
11258           const SMDS_MeshElement* curElem = backElemItr->next();
11259           if ( curElem && theElems.find(curElem) == theElems.end() &&
11260                ( bsc3d.get() ?
11261                  isInside( curElem, *bsc3d, aTol ) :
11262                  isInside( curElem, *aFaceClassifier, aTol )))
11263             theAffectedElems.insert( curElem );
11264         }
11265       }
11266     }
11267   }
11268   return true;
11269 }
11270
11271 //================================================================================
11272 /*!
11273   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11274   \param theElems - group of of elements (edges or faces) to be replicated
11275   \param theNodesNot - group of nodes not to replicate
11276   \param theShape - shape to detect affected elements (element which geometric center
11277   located on or inside shape).
11278   The replicated nodes should be associated to affected elements.
11279   \return TRUE if operation has been completed successfully, FALSE otherwise
11280 */
11281 //================================================================================
11282
11283 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11284                                             const TIDSortedElemSet& theNodesNot,
11285                                             const TopoDS_Shape&     theShape )
11286 {
11287   if ( theShape.IsNull() )
11288     return false;
11289
11290   const double aTol = Precision::Confusion();
11291   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11292   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11293   if ( theShape.ShapeType() == TopAbs_SOLID )
11294   {
11295     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11296     bsc3d->PerformInfinitePoint(aTol);
11297   }
11298   else if (theShape.ShapeType() == TopAbs_FACE )
11299   {
11300     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11301   }
11302
11303   // iterates on indicated elements and get elements by back references from their nodes
11304   TIDSortedElemSet anAffected;
11305   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11306   for ( ;  elemItr != theElems.end(); ++elemItr )
11307   {
11308     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11309     if (!anElem)
11310       continue;
11311
11312     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11313     while ( nodeItr->more() )
11314     {
11315       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11316       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11317         continue;
11318       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11319       while ( backElemItr->more() )
11320       {
11321         const SMDS_MeshElement* curElem = backElemItr->next();
11322         if ( curElem && theElems.find(curElem) == theElems.end() &&
11323              ( bsc3d ?
11324                isInside( curElem, *bsc3d, aTol ) :
11325                isInside( curElem, *aFaceClassifier, aTol )))
11326           anAffected.insert( curElem );
11327       }
11328     }
11329   }
11330   return DoubleNodes( theElems, theNodesNot, anAffected );
11331 }
11332
11333 /*!
11334  *  \brief compute an oriented angle between two planes defined by four points.
11335  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11336  *  @param p0 base of the rotation axe
11337  *  @param p1 extremity of the rotation axe
11338  *  @param g1 belongs to the first plane
11339  *  @param g2 belongs to the second plane
11340  */
11341 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11342 {
11343   gp_Vec vref(p0, p1);
11344   gp_Vec v1(p0, g1);
11345   gp_Vec v2(p0, g2);
11346   gp_Vec n1 = vref.Crossed(v1);
11347   gp_Vec n2 = vref.Crossed(v2);
11348   try {
11349     return n2.AngleWithRef(n1, vref);
11350   }
11351   catch ( Standard_Failure ) {
11352   }
11353   return Max( v1.Magnitude(), v2.Magnitude() );
11354 }
11355
11356 /*!
11357  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11358  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11359  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11360  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11361  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11362  * 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.
11363  * 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.
11364  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11365  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11366  * \param theElems - list of groups of volumes, where a group of volume is a set of
11367  *        SMDS_MeshElements sorted by Id.
11368  * \param createJointElems - if TRUE, create the elements
11369  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11370  *        the boundary between \a theDomains and the rest mesh
11371  * \return TRUE if operation has been completed successfully, FALSE otherwise
11372  */
11373 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11374                                                      bool                                 createJointElems,
11375                                                      bool                                 onAllBoundaries)
11376 {
11377   // MESSAGE("----------------------------------------------");
11378   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11379   // MESSAGE("----------------------------------------------");
11380
11381   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11382   meshDS->BuildDownWardConnectivity(true);
11383   CHRONO(50);
11384   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11385
11386   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11387   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11388   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11389
11390   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11391   std::map<int,int>celldom; // cell vtkId --> domain
11392   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11393   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11394   faceDomains.clear();
11395   celldom.clear();
11396   cellDomains.clear();
11397   nodeDomains.clear();
11398   std::map<int,int> emptyMap;
11399   std::set<int> emptySet;
11400   emptyMap.clear();
11401
11402   //MESSAGE(".. Number of domains :"<<theElems.size());
11403
11404   TIDSortedElemSet theRestDomElems;
11405   const int iRestDom  = -1;
11406   const int idom0     = onAllBoundaries ? iRestDom : 0;
11407   const int nbDomains = theElems.size();
11408
11409   // Check if the domains do not share an element
11410   for (int idom = 0; idom < nbDomains-1; idom++)
11411   {
11412     //       MESSAGE("... Check of domain #" << idom);
11413     const TIDSortedElemSet& domain = theElems[idom];
11414     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11415     for (; elemItr != domain.end(); ++elemItr)
11416     {
11417       const SMDS_MeshElement* anElem = *elemItr;
11418       int idombisdeb = idom + 1 ;
11419       // check if the element belongs to a domain further in the list
11420       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11421       {
11422         const TIDSortedElemSet& domainbis = theElems[idombis];
11423         if ( domainbis.count( anElem ))
11424         {
11425           MESSAGE(".... Domain #" << idom);
11426           MESSAGE(".... Domain #" << idombis);
11427           throw SALOME_Exception("The domains are not disjoint.");
11428           return false ;
11429         }
11430       }
11431     }
11432   }
11433
11434   for (int idom = 0; idom < nbDomains; idom++)
11435   {
11436
11437     // --- build a map (face to duplicate --> volume to modify)
11438     //     with all the faces shared by 2 domains (group of elements)
11439     //     and corresponding volume of this domain, for each shared face.
11440     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11441
11442     //MESSAGE("... Neighbors of domain #" << idom);
11443     const TIDSortedElemSet& domain = theElems[idom];
11444     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11445     for (; elemItr != domain.end(); ++elemItr)
11446     {
11447       const SMDS_MeshElement* anElem = *elemItr;
11448       if (!anElem)
11449         continue;
11450       int vtkId = anElem->GetVtkID();
11451       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11452       int neighborsVtkIds[NBMAXNEIGHBORS];
11453       int downIds[NBMAXNEIGHBORS];
11454       unsigned char downTypes[NBMAXNEIGHBORS];
11455       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11456       for (int n = 0; n < nbNeighbors; n++)
11457       {
11458         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11459         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11460         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11461         {
11462           bool ok = false;
11463           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11464           {
11465             // MESSAGE("Domain " << idombis);
11466             const TIDSortedElemSet& domainbis = theElems[idombis];
11467             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11468           }
11469           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11470           {
11471             DownIdType face(downIds[n], downTypes[n]);
11472             if (!faceDomains[face].count(idom))
11473             {
11474               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11475               celldom[vtkId] = idom;
11476               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11477             }
11478             if ( !ok )
11479             {
11480               theRestDomElems.insert( elem );
11481               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11482               celldom[neighborsVtkIds[n]] = iRestDom;
11483             }
11484           }
11485         }
11486       }
11487     }
11488   }
11489
11490   //MESSAGE("Number of shared faces " << faceDomains.size());
11491   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11492
11493   // --- explore the shared faces domain by domain,
11494   //     explore the nodes of the face and see if they belong to a cell in the domain,
11495   //     which has only a node or an edge on the border (not a shared face)
11496
11497   for (int idomain = idom0; idomain < nbDomains; idomain++)
11498   {
11499     //MESSAGE("Domain " << idomain);
11500     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11501     itface = faceDomains.begin();
11502     for (; itface != faceDomains.end(); ++itface)
11503     {
11504       const std::map<int, int>& domvol = itface->second;
11505       if (!domvol.count(idomain))
11506         continue;
11507       DownIdType face = itface->first;
11508       //MESSAGE(" --- face " << face.cellId);
11509       std::set<int> oldNodes;
11510       oldNodes.clear();
11511       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11512       std::set<int>::iterator itn = oldNodes.begin();
11513       for (; itn != oldNodes.end(); ++itn)
11514       {
11515         int oldId = *itn;
11516         //MESSAGE("     node " << oldId);
11517         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11518         for (int i=0; i<l.ncells; i++)
11519         {
11520           int vtkId = l.cells[i];
11521           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11522           if (!domain.count(anElem))
11523             continue;
11524           int vtkType = grid->GetCellType(vtkId);
11525           int downId = grid->CellIdToDownId(vtkId);
11526           if (downId < 0)
11527           {
11528             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11529             continue; // not OK at this stage of the algorithm:
11530             //no cells created after BuildDownWardConnectivity
11531           }
11532           DownIdType aCell(downId, vtkType);
11533           cellDomains[aCell][idomain] = vtkId;
11534           celldom[vtkId] = idomain;
11535           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11536         }
11537       }
11538     }
11539   }
11540
11541   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11542   //     for each shared face, get the nodes
11543   //     for each node, for each domain of the face, create a clone of the node
11544
11545   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11546   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11547   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11548
11549   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11550   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11551   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11552
11553   //MESSAGE(".. Duplication of the nodes");
11554   for (int idomain = idom0; idomain < nbDomains; idomain++)
11555   {
11556     itface = faceDomains.begin();
11557     for (; itface != faceDomains.end(); ++itface)
11558     {
11559       const std::map<int, int>& domvol = itface->second;
11560       if (!domvol.count(idomain))
11561         continue;
11562       DownIdType face = itface->first;
11563       //MESSAGE(" --- face " << face.cellId);
11564       std::set<int> oldNodes;
11565       oldNodes.clear();
11566       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11567       std::set<int>::iterator itn = oldNodes.begin();
11568       for (; itn != oldNodes.end(); ++itn)
11569       {
11570         int oldId = *itn;
11571         if (nodeDomains[oldId].empty())
11572         {
11573           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11574           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11575         }
11576         std::map<int, int>::const_iterator itdom = domvol.begin();
11577         for (; itdom != domvol.end(); ++itdom)
11578         {
11579           int idom = itdom->first;
11580           //MESSAGE("         domain " << idom);
11581           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11582           {
11583             if (nodeDomains[oldId].size() >= 2) // a multiple node
11584             {
11585               vector<int> orderedDoms;
11586               //MESSAGE("multiple node " << oldId);
11587               if (mutipleNodes.count(oldId))
11588                 orderedDoms = mutipleNodes[oldId];
11589               else
11590               {
11591                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11592                 for (; it != nodeDomains[oldId].end(); ++it)
11593                   orderedDoms.push_back(it->first);
11594               }
11595               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11596               //stringstream txt;
11597               //for (int i=0; i<orderedDoms.size(); i++)
11598               //  txt << orderedDoms[i] << " ";
11599               //MESSAGE("orderedDoms " << txt.str());
11600               mutipleNodes[oldId] = orderedDoms;
11601             }
11602             double *coords = grid->GetPoint(oldId);
11603             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11604             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11605             int newId = newNode->GetVtkID();
11606             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11607             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11608           }
11609         }
11610       }
11611     }
11612   }
11613
11614   //MESSAGE(".. Creation of elements");
11615   for (int idomain = idom0; idomain < nbDomains; idomain++)
11616   {
11617     itface = faceDomains.begin();
11618     for (; itface != faceDomains.end(); ++itface)
11619     {
11620       std::map<int, int> domvol = itface->second;
11621       if (!domvol.count(idomain))
11622         continue;
11623       DownIdType face = itface->first;
11624       //MESSAGE(" --- face " << face.cellId);
11625       std::set<int> oldNodes;
11626       oldNodes.clear();
11627       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11628       int nbMultipleNodes = 0;
11629       std::set<int>::iterator itn = oldNodes.begin();
11630       for (; itn != oldNodes.end(); ++itn)
11631       {
11632         int oldId = *itn;
11633         if (mutipleNodes.count(oldId))
11634           nbMultipleNodes++;
11635       }
11636       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11637       {
11638         //MESSAGE("multiple Nodes detected on a shared face");
11639         int downId = itface->first.cellId;
11640         unsigned char cellType = itface->first.cellType;
11641         // --- shared edge or shared face ?
11642         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11643         {
11644           int nodes[3];
11645           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11646           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11647             if (mutipleNodes.count(nodes[i]))
11648               if (!mutipleNodesToFace.count(nodes[i]))
11649                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11650         }
11651         else // shared face (between two volumes)
11652         {
11653           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11654           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11655           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11656           for (int ie =0; ie < nbEdges; ie++)
11657           {
11658             int nodes[3];
11659             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11660             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11661             {
11662               vector<int> vn0 = mutipleNodes[nodes[0]];
11663               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11664               vector<int> doms;
11665               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11666                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11667                   if ( vn0[i0] == vn1[i1] )
11668                     doms.push_back( vn0[ i0 ]);
11669               if ( doms.size() > 2 )
11670               {
11671                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11672                 double *coords = grid->GetPoint(nodes[0]);
11673                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11674                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11675                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11676                 gp_Pnt gref;
11677                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11678                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11679                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11680                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11681                 for ( size_t id = 0; id < doms.size(); id++ )
11682                 {
11683                   int idom = doms[id];
11684                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11685                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11686                   {
11687                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11688                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11689                     if (domain.count(elem))
11690                     {
11691                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11692                       domvol[idom] = (SMDS_MeshVolume*) svol;
11693                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11694                       double values[3] = { 0,0,0 };
11695                       vtkIdType npts = 0;
11696                       vtkIdType* pts = 0;
11697                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11698                       for ( vtkIdType i = 0; i < npts; ++i )
11699                       {
11700                         double *coords = grid->GetPoint( pts[i] );
11701                         for ( int j = 0; j < 3; ++j )
11702                           values[j] += coords[j] / npts;
11703                       }
11704                       if ( id == 0 )
11705                       {
11706                         gref.SetCoord( values[0], values[1], values[2] );
11707                         angleDom[idom] = 0;
11708                       }
11709                       else
11710                       {
11711                         gp_Pnt g( values[0], values[1], values[2] );
11712                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11713                         //MESSAGE("  angle=" << angleDom[idom]);
11714                       }
11715                       break;
11716                     }
11717                   }
11718                 }
11719                 map<double, int> sortedDom; // sort domains by angle
11720                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11721                   sortedDom[ia->second] = ia->first;
11722                 vector<int> vnodes;
11723                 vector<int> vdom;
11724                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11725                 {
11726                   vdom.push_back(ib->second);
11727                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11728                 }
11729                 for (int ino = 0; ino < nbNodes; ino++)
11730                   vnodes.push_back(nodes[ino]);
11731                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11732               }
11733             }
11734           }
11735         }
11736       }
11737     }
11738   }
11739
11740   // --- iterate on shared faces (volumes to modify, face to extrude)
11741   //     get node id's of the face (id SMDS = id VTK)
11742   //     create flat element with old and new nodes if requested
11743
11744   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11745   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11746
11747   std::map<int, std::map<long,int> > nodeQuadDomains;
11748   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11749
11750   //MESSAGE(".. Creation of elements: simple junction");
11751   if (createJointElems)
11752   {
11753     string joints2DName = "joints2D";
11754     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11755     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11756     string joints3DName = "joints3D";
11757     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11758     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11759
11760     itface = faceDomains.begin();
11761     for (; itface != faceDomains.end(); ++itface)
11762     {
11763       DownIdType face = itface->first;
11764       std::set<int> oldNodes;
11765       std::set<int>::iterator itn;
11766       oldNodes.clear();
11767       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11768
11769       std::map<int, int> domvol = itface->second;
11770       std::map<int, int>::iterator itdom = domvol.begin();
11771       int dom1 = itdom->first;
11772       int vtkVolId = itdom->second;
11773       itdom++;
11774       int dom2 = itdom->first;
11775       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11776                                                        nodeQuadDomains);
11777       stringstream grpname;
11778       grpname << "j_";
11779       if (dom1 < dom2)
11780         grpname << dom1 << "_" << dom2;
11781       else
11782         grpname << dom2 << "_" << dom1;
11783       string namegrp = grpname.str();
11784       if (!mapOfJunctionGroups.count(namegrp))
11785         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11786       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11787       if (sgrp)
11788         sgrp->Add(vol->GetID());
11789       if (vol->GetType() == SMDSAbs_Volume)
11790         joints3DGrp->Add(vol->GetID());
11791       else if (vol->GetType() == SMDSAbs_Face)
11792         joints2DGrp->Add(vol->GetID());
11793     }
11794   }
11795
11796   // --- create volumes on multiple domain intersection if requested
11797   //     iterate on mutipleNodesToFace
11798   //     iterate on edgesMultiDomains
11799
11800   //MESSAGE(".. Creation of elements: multiple junction");
11801   if (createJointElems)
11802   {
11803     // --- iterate on mutipleNodesToFace
11804
11805     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11806     for (; itn != mutipleNodesToFace.end(); ++itn)
11807     {
11808       int node = itn->first;
11809       vector<int> orderDom = itn->second;
11810       vector<vtkIdType> orderedNodes;
11811       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11812         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11813       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11814
11815       stringstream grpname;
11816       grpname << "m2j_";
11817       grpname << 0 << "_" << 0;
11818       string namegrp = grpname.str();
11819       if (!mapOfJunctionGroups.count(namegrp))
11820         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11821       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11822       if (sgrp)
11823         sgrp->Add(face->GetID());
11824     }
11825
11826     // --- iterate on edgesMultiDomains
11827
11828     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11829     for (; ite != edgesMultiDomains.end(); ++ite)
11830     {
11831       vector<int> nodes = ite->first;
11832       vector<int> orderDom = ite->second;
11833       vector<vtkIdType> orderedNodes;
11834       if (nodes.size() == 2)
11835       {
11836         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11837         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11838           if ( orderDom.size() == 3 )
11839             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11840               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11841           else
11842             for (int idom = orderDom.size()-1; idom >=0; idom--)
11843               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11844         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11845
11846         string namegrp = "jointsMultiples";
11847         if (!mapOfJunctionGroups.count(namegrp))
11848           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11849         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11850         if (sgrp)
11851           sgrp->Add(vol->GetID());
11852       }
11853       else
11854       {
11855         //INFOS("Quadratic multiple joints not implemented");
11856         // TODO quadratic nodes
11857       }
11858     }
11859   }
11860
11861   // --- list the explicit faces and edges of the mesh that need to be modified,
11862   //     i.e. faces and edges built with one or more duplicated nodes.
11863   //     associate these faces or edges to their corresponding domain.
11864   //     only the first domain found is kept when a face or edge is shared
11865
11866   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11867   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11868   faceOrEdgeDom.clear();
11869   feDom.clear();
11870
11871   //MESSAGE(".. Modification of elements");
11872   for (int idomain = idom0; idomain < nbDomains; idomain++)
11873   {
11874     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11875     for (; itnod != nodeDomains.end(); ++itnod)
11876     {
11877       int oldId = itnod->first;
11878       //MESSAGE("     node " << oldId);
11879       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11880       for (int i = 0; i < l.ncells; i++)
11881       {
11882         int vtkId = l.cells[i];
11883         int vtkType = grid->GetCellType(vtkId);
11884         int downId = grid->CellIdToDownId(vtkId);
11885         if (downId < 0)
11886           continue; // new cells: not to be modified
11887         DownIdType aCell(downId, vtkType);
11888         int volParents[1000];
11889         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11890         for (int j = 0; j < nbvol; j++)
11891           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11892             if (!feDom.count(vtkId))
11893             {
11894               feDom[vtkId] = idomain;
11895               faceOrEdgeDom[aCell] = emptyMap;
11896               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11897               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11898               //        << " type " << vtkType << " downId " << downId);
11899             }
11900       }
11901     }
11902   }
11903
11904   // --- iterate on shared faces (volumes to modify, face to extrude)
11905   //     get node id's of the face
11906   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11907
11908   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11909   for (int m=0; m<3; m++)
11910   {
11911     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11912     itface = (*amap).begin();
11913     for (; itface != (*amap).end(); ++itface)
11914     {
11915       DownIdType face = itface->first;
11916       std::set<int> oldNodes;
11917       std::set<int>::iterator itn;
11918       oldNodes.clear();
11919       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11920       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11921       std::map<int, int> localClonedNodeIds;
11922
11923       std::map<int, int> domvol = itface->second;
11924       std::map<int, int>::iterator itdom = domvol.begin();
11925       for (; itdom != domvol.end(); ++itdom)
11926       {
11927         int idom = itdom->first;
11928         int vtkVolId = itdom->second;
11929         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11930         localClonedNodeIds.clear();
11931         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11932         {
11933           int oldId = *itn;
11934           if (nodeDomains[oldId].count(idom))
11935           {
11936             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11937             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11938           }
11939         }
11940         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11941       }
11942     }
11943   }
11944
11945   // Remove empty groups (issue 0022812)
11946   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11947   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11948   {
11949     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11950       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11951   }
11952
11953   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11954   grid->DeleteLinks();
11955
11956   CHRONOSTOP(50);
11957   counters::stats();
11958   return true;
11959 }
11960
11961 /*!
11962  * \brief Double nodes on some external faces and create flat elements.
11963  * Flat elements are mainly used by some types of mechanic calculations.
11964  *
11965  * Each group of the list must be constituted of faces.
11966  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11967  * @param theElems - list of groups of faces, where a group of faces is a set of
11968  * SMDS_MeshElements sorted by Id.
11969  * @return TRUE if operation has been completed successfully, FALSE otherwise
11970  */
11971 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11972 {
11973   // MESSAGE("-------------------------------------------------");
11974   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11975   // MESSAGE("-------------------------------------------------");
11976
11977   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11978
11979   // --- For each group of faces
11980   //     duplicate the nodes, create a flat element based on the face
11981   //     replace the nodes of the faces by their clones
11982
11983   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11984   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11985   clonedNodes.clear();
11986   intermediateNodes.clear();
11987   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11988   mapOfJunctionGroups.clear();
11989
11990   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11991   {
11992     const TIDSortedElemSet&           domain = theElems[idom];
11993     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11994     for ( ; elemItr != domain.end(); ++elemItr )
11995     {
11996       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11997       if (!aFace)
11998         continue;
11999       // MESSAGE("aFace=" << aFace->GetID());
12000       bool isQuad = aFace->IsQuadratic();
12001       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12002
12003       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12004
12005       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12006       while (nodeIt->more())
12007       {
12008         const SMDS_MeshNode* node = nodeIt->next();
12009         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12010         if (isMedium)
12011           ln2.push_back(node);
12012         else
12013           ln0.push_back(node);
12014
12015         const SMDS_MeshNode* clone = 0;
12016         if (!clonedNodes.count(node))
12017         {
12018           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12019           copyPosition( node, clone );
12020           clonedNodes[node] = clone;
12021         }
12022         else
12023           clone = clonedNodes[node];
12024
12025         if (isMedium)
12026           ln3.push_back(clone);
12027         else
12028           ln1.push_back(clone);
12029
12030         const SMDS_MeshNode* inter = 0;
12031         if (isQuad && (!isMedium))
12032         {
12033           if (!intermediateNodes.count(node))
12034           {
12035             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12036             copyPosition( node, inter );
12037             intermediateNodes[node] = inter;
12038           }
12039           else
12040             inter = intermediateNodes[node];
12041           ln4.push_back(inter);
12042         }
12043       }
12044
12045       // --- extrude the face
12046
12047       vector<const SMDS_MeshNode*> ln;
12048       SMDS_MeshVolume* vol = 0;
12049       vtkIdType aType = aFace->GetVtkType();
12050       switch (aType)
12051       {
12052       case VTK_TRIANGLE:
12053         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12054         // MESSAGE("vol prism " << vol->GetID());
12055         ln.push_back(ln1[0]);
12056         ln.push_back(ln1[1]);
12057         ln.push_back(ln1[2]);
12058         break;
12059       case VTK_QUAD:
12060         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12061         // MESSAGE("vol hexa " << vol->GetID());
12062         ln.push_back(ln1[0]);
12063         ln.push_back(ln1[1]);
12064         ln.push_back(ln1[2]);
12065         ln.push_back(ln1[3]);
12066         break;
12067       case VTK_QUADRATIC_TRIANGLE:
12068         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12069                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12070         // MESSAGE("vol quad prism " << vol->GetID());
12071         ln.push_back(ln1[0]);
12072         ln.push_back(ln1[1]);
12073         ln.push_back(ln1[2]);
12074         ln.push_back(ln3[0]);
12075         ln.push_back(ln3[1]);
12076         ln.push_back(ln3[2]);
12077         break;
12078       case VTK_QUADRATIC_QUAD:
12079         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12080         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12081         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12082         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12083                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12084                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12085         // MESSAGE("vol quad hexa " << vol->GetID());
12086         ln.push_back(ln1[0]);
12087         ln.push_back(ln1[1]);
12088         ln.push_back(ln1[2]);
12089         ln.push_back(ln1[3]);
12090         ln.push_back(ln3[0]);
12091         ln.push_back(ln3[1]);
12092         ln.push_back(ln3[2]);
12093         ln.push_back(ln3[3]);
12094         break;
12095       case VTK_POLYGON:
12096         break;
12097       default:
12098         break;
12099       }
12100
12101       if (vol)
12102       {
12103         stringstream grpname;
12104         grpname << "jf_";
12105         grpname << idom;
12106         string namegrp = grpname.str();
12107         if (!mapOfJunctionGroups.count(namegrp))
12108           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12109         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12110         if (sgrp)
12111           sgrp->Add(vol->GetID());
12112       }
12113
12114       // --- modify the face
12115
12116       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12117     }
12118   }
12119   return true;
12120 }
12121
12122 /*!
12123  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12124  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12125  *  groups of faces to remove inside the object, (idem edges).
12126  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12127  */
12128 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12129                                       const TopoDS_Shape&             theShape,
12130                                       SMESH_NodeSearcher*             theNodeSearcher,
12131                                       const char*                     groupName,
12132                                       std::vector<double>&            nodesCoords,
12133                                       std::vector<std::vector<int> >& listOfListOfNodes)
12134 {
12135   // MESSAGE("--------------------------------");
12136   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12137   // MESSAGE("--------------------------------");
12138
12139   // --- zone of volumes to remove is given :
12140   //     1 either by a geom shape (one or more vertices) and a radius,
12141   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12142   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12143   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12144   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12145   //     defined by it's name.
12146
12147   SMESHDS_GroupBase* groupDS = 0;
12148   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12149   while ( groupIt->more() )
12150   {
12151     groupDS = 0;
12152     SMESH_Group * group = groupIt->next();
12153     if ( !group ) continue;
12154     groupDS = group->GetGroupDS();
12155     if ( !groupDS || groupDS->IsEmpty() ) continue;
12156     std::string grpName = group->GetName();
12157     //MESSAGE("grpName=" << grpName);
12158     if (grpName == groupName)
12159       break;
12160     else
12161       groupDS = 0;
12162   }
12163
12164   bool isNodeGroup = false;
12165   bool isNodeCoords = false;
12166   if (groupDS)
12167   {
12168     if (groupDS->GetType() != SMDSAbs_Node)
12169       return;
12170     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12171   }
12172
12173   if (nodesCoords.size() > 0)
12174     isNodeCoords = true; // a list o nodes given by their coordinates
12175   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12176
12177   // --- define groups to build
12178
12179   // --- group of SMDS volumes
12180   string grpvName = groupName;
12181   grpvName += "_vol";
12182   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12183   if (!grp)
12184   {
12185     MESSAGE("group not created " << grpvName);
12186     return;
12187   }
12188   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12189
12190   // --- group of SMDS faces on the skin
12191   string grpsName = groupName;
12192   grpsName += "_skin";
12193   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12194   if (!grps)
12195   {
12196     MESSAGE("group not created " << grpsName);
12197     return;
12198   }
12199   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12200
12201   // --- group of SMDS faces internal (several shapes)
12202   string grpiName = groupName;
12203   grpiName += "_internalFaces";
12204   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12205   if (!grpi)
12206   {
12207     MESSAGE("group not created " << grpiName);
12208     return;
12209   }
12210   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12211
12212   // --- group of SMDS faces internal (several shapes)
12213   string grpeiName = groupName;
12214   grpeiName += "_internalEdges";
12215   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12216   if (!grpei)
12217   {
12218     MESSAGE("group not created " << grpeiName);
12219     return;
12220   }
12221   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12222
12223   // --- build downward connectivity
12224
12225   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12226   meshDS->BuildDownWardConnectivity(true);
12227   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12228
12229   // --- set of volumes detected inside
12230
12231   std::set<int> setOfInsideVol;
12232   std::set<int> setOfVolToCheck;
12233
12234   std::vector<gp_Pnt> gpnts;
12235   gpnts.clear();
12236
12237   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12238   {
12239     //MESSAGE("group of nodes provided");
12240     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12241     while ( elemIt->more() )
12242     {
12243       const SMDS_MeshElement* elem = elemIt->next();
12244       if (!elem)
12245         continue;
12246       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12247       if (!node)
12248         continue;
12249       SMDS_MeshElement* vol = 0;
12250       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12251       while (volItr->more())
12252       {
12253         vol = (SMDS_MeshElement*)volItr->next();
12254         setOfInsideVol.insert(vol->GetVtkID());
12255         sgrp->Add(vol->GetID());
12256       }
12257     }
12258   }
12259   else if (isNodeCoords)
12260   {
12261     //MESSAGE("list of nodes coordinates provided");
12262     size_t i = 0;
12263     int k = 0;
12264     while ( i < nodesCoords.size()-2 )
12265     {
12266       double x = nodesCoords[i++];
12267       double y = nodesCoords[i++];
12268       double z = nodesCoords[i++];
12269       gp_Pnt p = gp_Pnt(x, y ,z);
12270       gpnts.push_back(p);
12271       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12272       k++;
12273     }
12274   }
12275   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12276   {
12277     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12278     TopTools_IndexedMapOfShape vertexMap;
12279     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12280     gp_Pnt p = gp_Pnt(0,0,0);
12281     if (vertexMap.Extent() < 1)
12282       return;
12283
12284     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12285     {
12286       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12287       p = BRep_Tool::Pnt(vertex);
12288       gpnts.push_back(p);
12289       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12290     }
12291   }
12292
12293   if (gpnts.size() > 0)
12294   {
12295     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12296     //MESSAGE("startNode->nodeId " << nodeId);
12297
12298     double radius2 = radius*radius;
12299     //MESSAGE("radius2 " << radius2);
12300
12301     // --- volumes on start node
12302
12303     setOfVolToCheck.clear();
12304     SMDS_MeshElement* startVol = 0;
12305     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12306     while (volItr->more())
12307     {
12308       startVol = (SMDS_MeshElement*)volItr->next();
12309       setOfVolToCheck.insert(startVol->GetVtkID());
12310     }
12311     if (setOfVolToCheck.empty())
12312     {
12313       MESSAGE("No volumes found");
12314       return;
12315     }
12316
12317     // --- starting with central volumes then their neighbors, check if they are inside
12318     //     or outside the domain, until no more new neighbor volume is inside.
12319     //     Fill the group of inside volumes
12320
12321     std::map<int, double> mapOfNodeDistance2;
12322     mapOfNodeDistance2.clear();
12323     std::set<int> setOfOutsideVol;
12324     while (!setOfVolToCheck.empty())
12325     {
12326       std::set<int>::iterator it = setOfVolToCheck.begin();
12327       int vtkId = *it;
12328       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12329       bool volInside = false;
12330       vtkIdType npts = 0;
12331       vtkIdType* pts = 0;
12332       grid->GetCellPoints(vtkId, npts, pts);
12333       for (int i=0; i<npts; i++)
12334       {
12335         double distance2 = 0;
12336         if (mapOfNodeDistance2.count(pts[i]))
12337         {
12338           distance2 = mapOfNodeDistance2[pts[i]];
12339           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12340         }
12341         else
12342         {
12343           double *coords = grid->GetPoint(pts[i]);
12344           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12345           distance2 = 1.E40;
12346           for ( size_t j = 0; j < gpnts.size(); j++ )
12347           {
12348             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12349             if (d2 < distance2)
12350             {
12351               distance2 = d2;
12352               if (distance2 < radius2)
12353                 break;
12354             }
12355           }
12356           mapOfNodeDistance2[pts[i]] = distance2;
12357           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12358         }
12359         if (distance2 < radius2)
12360         {
12361           volInside = true; // one or more nodes inside the domain
12362           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12363           break;
12364         }
12365       }
12366       if (volInside)
12367       {
12368         setOfInsideVol.insert(vtkId);
12369         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12370         int neighborsVtkIds[NBMAXNEIGHBORS];
12371         int downIds[NBMAXNEIGHBORS];
12372         unsigned char downTypes[NBMAXNEIGHBORS];
12373         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12374         for (int n = 0; n < nbNeighbors; n++)
12375           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12376             setOfVolToCheck.insert(neighborsVtkIds[n]);
12377       }
12378       else
12379       {
12380         setOfOutsideVol.insert(vtkId);
12381         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12382       }
12383       setOfVolToCheck.erase(vtkId);
12384     }
12385   }
12386
12387   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12388   //     If yes, add the volume to the inside set
12389
12390   bool addedInside = true;
12391   std::set<int> setOfVolToReCheck;
12392   while (addedInside)
12393   {
12394     //MESSAGE(" --------------------------- re check");
12395     addedInside = false;
12396     std::set<int>::iterator itv = setOfInsideVol.begin();
12397     for (; itv != setOfInsideVol.end(); ++itv)
12398     {
12399       int vtkId = *itv;
12400       int neighborsVtkIds[NBMAXNEIGHBORS];
12401       int downIds[NBMAXNEIGHBORS];
12402       unsigned char downTypes[NBMAXNEIGHBORS];
12403       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12404       for (int n = 0; n < nbNeighbors; n++)
12405         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12406           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12407     }
12408     setOfVolToCheck = setOfVolToReCheck;
12409     setOfVolToReCheck.clear();
12410     while  (!setOfVolToCheck.empty())
12411     {
12412       std::set<int>::iterator it = setOfVolToCheck.begin();
12413       int vtkId = *it;
12414       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12415       {
12416         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12417         int countInside = 0;
12418         int neighborsVtkIds[NBMAXNEIGHBORS];
12419         int downIds[NBMAXNEIGHBORS];
12420         unsigned char downTypes[NBMAXNEIGHBORS];
12421         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12422         for (int n = 0; n < nbNeighbors; n++)
12423           if (setOfInsideVol.count(neighborsVtkIds[n]))
12424             countInside++;
12425         //MESSAGE("countInside " << countInside);
12426         if (countInside > 1)
12427         {
12428           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12429           setOfInsideVol.insert(vtkId);
12430           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12431           addedInside = true;
12432         }
12433         else
12434           setOfVolToReCheck.insert(vtkId);
12435       }
12436       setOfVolToCheck.erase(vtkId);
12437     }
12438   }
12439
12440   // --- map of Downward faces at the boundary, inside the global volume
12441   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12442   //     fill group of SMDS faces inside the volume (when several volume shapes)
12443   //     fill group of SMDS faces on the skin of the global volume (if skin)
12444
12445   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12446   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12447   std::set<int>::iterator it = setOfInsideVol.begin();
12448   for (; it != setOfInsideVol.end(); ++it)
12449   {
12450     int vtkId = *it;
12451     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12452     int neighborsVtkIds[NBMAXNEIGHBORS];
12453     int downIds[NBMAXNEIGHBORS];
12454     unsigned char downTypes[NBMAXNEIGHBORS];
12455     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12456     for (int n = 0; n < nbNeighbors; n++)
12457     {
12458       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12459       if (neighborDim == 3)
12460       {
12461         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12462         {
12463           DownIdType face(downIds[n], downTypes[n]);
12464           boundaryFaces[face] = vtkId;
12465         }
12466         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12467         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12468         if (vtkFaceId >= 0)
12469         {
12470           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12471           // find also the smds edges on this face
12472           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12473           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12474           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12475           for (int i = 0; i < nbEdges; i++)
12476           {
12477             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12478             if (vtkEdgeId >= 0)
12479               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12480           }
12481         }
12482       }
12483       else if (neighborDim == 2) // skin of the volume
12484       {
12485         DownIdType face(downIds[n], downTypes[n]);
12486         skinFaces[face] = vtkId;
12487         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12488         if (vtkFaceId >= 0)
12489           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12490       }
12491     }
12492   }
12493
12494   // --- identify the edges constituting the wire of each subshape on the skin
12495   //     define polylines with the nodes of edges, equivalent to wires
12496   //     project polylines on subshapes, and partition, to get geom faces
12497
12498   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12499   std::set<int> emptySet;
12500   emptySet.clear();
12501   std::set<int> shapeIds;
12502
12503   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12504   while (itelem->more())
12505   {
12506     const SMDS_MeshElement *elem = itelem->next();
12507     int shapeId = elem->getshapeId();
12508     int   vtkId = elem->GetVtkID();
12509     if (!shapeIdToVtkIdSet.count(shapeId))
12510     {
12511       shapeIdToVtkIdSet[shapeId] = emptySet;
12512       shapeIds.insert(shapeId);
12513     }
12514     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12515   }
12516
12517   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12518   std::set<DownIdType, DownIdCompare> emptyEdges;
12519   emptyEdges.clear();
12520
12521   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12522   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12523   {
12524     int shapeId = itShape->first;
12525     //MESSAGE(" --- Shape ID --- "<< shapeId);
12526     shapeIdToEdges[shapeId] = emptyEdges;
12527
12528     std::vector<int> nodesEdges;
12529
12530     std::set<int>::iterator its = itShape->second.begin();
12531     for (; its != itShape->second.end(); ++its)
12532     {
12533       int vtkId = *its;
12534       //MESSAGE("     " << vtkId);
12535       int neighborsVtkIds[NBMAXNEIGHBORS];
12536       int downIds[NBMAXNEIGHBORS];
12537       unsigned char downTypes[NBMAXNEIGHBORS];
12538       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12539       for (int n = 0; n < nbNeighbors; n++)
12540       {
12541         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12542           continue;
12543         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12544         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12545         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12546         {
12547           DownIdType edge(downIds[n], downTypes[n]);
12548           if (!shapeIdToEdges[shapeId].count(edge))
12549           {
12550             shapeIdToEdges[shapeId].insert(edge);
12551             int vtkNodeId[3];
12552             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12553             nodesEdges.push_back(vtkNodeId[0]);
12554             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12555             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12556           }
12557         }
12558       }
12559     }
12560
12561     std::list<int> order;
12562     order.clear();
12563     if (nodesEdges.size() > 0)
12564     {
12565       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12566       nodesEdges[0] = -1;
12567       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12568       nodesEdges[1] = -1; // do not reuse this edge
12569       bool found = true;
12570       while (found)
12571       {
12572         int nodeTofind = order.back(); // try first to push back
12573         int i = 0;
12574         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12575           if (nodesEdges[i] == nodeTofind)
12576             break;
12577         if ( i == (int) nodesEdges.size() )
12578           found = false; // no follower found on back
12579         else
12580         {
12581           if (i%2) // odd ==> use the previous one
12582             if (nodesEdges[i-1] < 0)
12583               found = false;
12584             else
12585             {
12586               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12587               nodesEdges[i-1] = -1;
12588             }
12589           else // even ==> use the next one
12590             if (nodesEdges[i+1] < 0)
12591               found = false;
12592             else
12593             {
12594               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12595               nodesEdges[i+1] = -1;
12596             }
12597         }
12598         if (found)
12599           continue;
12600         // try to push front
12601         found = true;
12602         nodeTofind = order.front(); // try to push front
12603         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12604           if ( nodesEdges[i] == nodeTofind )
12605             break;
12606         if ( i == (int)nodesEdges.size() )
12607         {
12608           found = false; // no predecessor found on front
12609           continue;
12610         }
12611         if (i%2) // odd ==> use the previous one
12612           if (nodesEdges[i-1] < 0)
12613             found = false;
12614           else
12615           {
12616             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12617             nodesEdges[i-1] = -1;
12618           }
12619         else // even ==> use the next one
12620           if (nodesEdges[i+1] < 0)
12621             found = false;
12622           else
12623           {
12624             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12625             nodesEdges[i+1] = -1;
12626           }
12627       }
12628     }
12629
12630
12631     std::vector<int> nodes;
12632     nodes.push_back(shapeId);
12633     std::list<int>::iterator itl = order.begin();
12634     for (; itl != order.end(); itl++)
12635     {
12636       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12637       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12638     }
12639     listOfListOfNodes.push_back(nodes);
12640   }
12641
12642   //     partition geom faces with blocFissure
12643   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12644   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12645
12646   return;
12647 }
12648
12649
12650 //================================================================================
12651 /*!
12652  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12653  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12654  * \return TRUE if operation has been completed successfully, FALSE otherwise
12655  */
12656 //================================================================================
12657
12658 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12659 {
12660   // iterates on volume elements and detect all free faces on them
12661   SMESHDS_Mesh* aMesh = GetMeshDS();
12662   if (!aMesh)
12663     return false;
12664
12665   ElemFeatures faceType( SMDSAbs_Face );
12666   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12667   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12668   while(vIt->more())
12669   {
12670     const SMDS_MeshVolume* volume = vIt->next();
12671     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12672     vTool.SetExternalNormal();
12673     const int iQuad = volume->IsQuadratic();
12674     faceType.SetQuad( iQuad );
12675     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12676     {
12677       if (!vTool.IsFreeFace(iface))
12678         continue;
12679       nbFree++;
12680       vector<const SMDS_MeshNode *> nodes;
12681       int nbFaceNodes = vTool.NbFaceNodes(iface);
12682       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12683       int inode = 0;
12684       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12685         nodes.push_back(faceNodes[inode]);
12686
12687       if (iQuad) // add medium nodes
12688       {
12689         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12690           nodes.push_back(faceNodes[inode]);
12691         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12692           nodes.push_back(faceNodes[8]);
12693       }
12694       // add new face based on volume nodes
12695       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12696       {
12697         nbExisted++; // face already exists
12698       }
12699       else
12700       {
12701         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12702         nbCreated++;
12703       }
12704     }
12705   }
12706   return ( nbFree == ( nbExisted + nbCreated ));
12707 }
12708
12709 namespace
12710 {
12711   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12712   {
12713     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12714       return n;
12715     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12716   }
12717 }
12718 //================================================================================
12719 /*!
12720  * \brief Creates missing boundary elements
12721  *  \param elements - elements whose boundary is to be checked
12722  *  \param dimension - defines type of boundary elements to create
12723  *  \param group - a group to store created boundary elements in
12724  *  \param targetMesh - a mesh to store created boundary elements in
12725  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12726  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12727  *                                boundary elements will be copied into the targetMesh
12728  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12729  *                                boundary elements will be added into the new group
12730  *  \param aroundElements - if true, elements will be created on boundary of given
12731  *                          elements else, on boundary of the whole mesh.
12732  * \return nb of added boundary elements
12733  */
12734 //================================================================================
12735
12736 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12737                                        Bnd_Dimension           dimension,
12738                                        SMESH_Group*            group/*=0*/,
12739                                        SMESH_Mesh*             targetMesh/*=0*/,
12740                                        bool                    toCopyElements/*=false*/,
12741                                        bool                    toCopyExistingBoundary/*=false*/,
12742                                        bool                    toAddExistingBondary/*= false*/,
12743                                        bool                    aroundElements/*= false*/)
12744 {
12745   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12746   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12747   // hope that all elements are of the same type, do not check them all
12748   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12749     throw SALOME_Exception(LOCALIZED("wrong element type"));
12750
12751   if ( !targetMesh )
12752     toCopyElements = toCopyExistingBoundary = false;
12753
12754   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12755   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12756   int nbAddedBnd = 0;
12757
12758   // editor adding present bnd elements and optionally holding elements to add to the group
12759   SMESH_MeshEditor* presentEditor;
12760   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12761   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12762
12763   SMESH_MesherHelper helper( *myMesh );
12764   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12765   SMDS_VolumeTool vTool;
12766   TIDSortedElemSet avoidSet;
12767   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12768   size_t inode;
12769
12770   typedef vector<const SMDS_MeshNode*> TConnectivity;
12771   TConnectivity tgtNodes;
12772   ElemFeatures elemKind( missType ), elemToCopy;
12773
12774   vector<const SMDS_MeshElement*> presentBndElems;
12775   vector<TConnectivity>           missingBndElems;
12776   vector<int>                     freeFacets;
12777   TConnectivity nodes, elemNodes;
12778
12779   SMDS_ElemIteratorPtr eIt;
12780   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12781   else                  eIt = SMESHUtils::elemSetIterator( elements );
12782
12783   while ( eIt->more() )
12784   {
12785     const SMDS_MeshElement* elem = eIt->next();
12786     const int              iQuad = elem->IsQuadratic();
12787     elemKind.SetQuad( iQuad );
12788
12789     // ------------------------------------------------------------------------------------
12790     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12791     // ------------------------------------------------------------------------------------
12792     presentBndElems.clear();
12793     missingBndElems.clear();
12794     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12795     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12796     {
12797       const SMDS_MeshElement* otherVol = 0;
12798       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12799       {
12800         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12801              ( !aroundElements || elements.count( otherVol )))
12802           continue;
12803         freeFacets.push_back( iface );
12804       }
12805       if ( missType == SMDSAbs_Face )
12806         vTool.SetExternalNormal();
12807       for ( size_t i = 0; i < freeFacets.size(); ++i )
12808       {
12809         int                iface = freeFacets[i];
12810         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12811         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12812         if ( missType == SMDSAbs_Edge ) // boundary edges
12813         {
12814           nodes.resize( 2+iQuad );
12815           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12816           {
12817             for ( size_t j = 0; j < nodes.size(); ++j )
12818               nodes[ j ] = nn[ i+j ];
12819             if ( const SMDS_MeshElement* edge =
12820                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12821               presentBndElems.push_back( edge );
12822             else
12823               missingBndElems.push_back( nodes );
12824           }
12825         }
12826         else // boundary face
12827         {
12828           nodes.clear();
12829           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12830             nodes.push_back( nn[inode] ); // add corner nodes
12831           if (iQuad)
12832             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12833               nodes.push_back( nn[inode] ); // add medium nodes
12834           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12835           if ( iCenter > 0 )
12836             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12837
12838           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12839                                                                SMDSAbs_Face, /*noMedium=*/false ))
12840             presentBndElems.push_back( f );
12841           else
12842             missingBndElems.push_back( nodes );
12843
12844           if ( targetMesh != myMesh )
12845           {
12846             // add 1D elements on face boundary to be added to a new mesh
12847             const SMDS_MeshElement* edge;
12848             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12849             {
12850               if ( iQuad )
12851                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12852               else
12853                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12854               if ( edge && avoidSet.insert( edge ).second )
12855                 presentBndElems.push_back( edge );
12856             }
12857           }
12858         }
12859       }
12860     }
12861     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12862     {
12863       avoidSet.clear(), avoidSet.insert( elem );
12864       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12865                         SMDS_MeshElement::iterator() );
12866       elemNodes.push_back( elemNodes[0] );
12867       nodes.resize( 2 + iQuad );
12868       const int nbLinks = elem->NbCornerNodes();
12869       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12870       {
12871         nodes[0] = elemNodes[iN];
12872         nodes[1] = elemNodes[iN+1+iQuad];
12873         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12874           continue; // not free link
12875
12876         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12877         if ( const SMDS_MeshElement* edge =
12878              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12879           presentBndElems.push_back( edge );
12880         else
12881           missingBndElems.push_back( nodes );
12882       }
12883     }
12884
12885     // ---------------------------------
12886     // 2. Add missing boundary elements
12887     // ---------------------------------
12888     if ( targetMesh != myMesh )
12889       // instead of making a map of nodes in this mesh and targetMesh,
12890       // we create nodes with same IDs.
12891       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12892       {
12893         TConnectivity& srcNodes = missingBndElems[i];
12894         tgtNodes.resize( srcNodes.size() );
12895         for ( inode = 0; inode < srcNodes.size(); ++inode )
12896           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12897         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12898                                                                    missType,
12899                                                                    /*noMedium=*/false))
12900           continue;
12901         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12902         ++nbAddedBnd;
12903       }
12904     else
12905       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12906       {
12907         TConnectivity& nodes = missingBndElems[ i ];
12908         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12909                                                                    missType,
12910                                                                    /*noMedium=*/false))
12911           continue;
12912         SMDS_MeshElement* newElem =
12913           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12914         nbAddedBnd += bool( newElem );
12915
12916         // try to set a new element to a shape
12917         if ( myMesh->HasShapeToMesh() )
12918         {
12919           bool ok = true;
12920           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12921           const size_t nbN = nodes.size() / (iQuad+1 );
12922           for ( inode = 0; inode < nbN && ok; ++inode )
12923           {
12924             pair<int, TopAbs_ShapeEnum> i_stype =
12925               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12926             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12927               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12928           }
12929           if ( ok && mediumShapes.size() > 1 )
12930           {
12931             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12932             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12933             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12934             {
12935               if (( ok = ( stype_i->first != stype_i_0.first )))
12936                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12937                                         aMesh->IndexToShape( stype_i_0.second ));
12938             }
12939           }
12940           if ( ok && mediumShapes.begin()->first == missShapeType )
12941             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12942         }
12943       }
12944
12945     // ----------------------------------
12946     // 3. Copy present boundary elements
12947     // ----------------------------------
12948     if ( toCopyExistingBoundary )
12949       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12950       {
12951         const SMDS_MeshElement* e = presentBndElems[i];
12952         tgtNodes.resize( e->NbNodes() );
12953         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12954           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12955         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12956       }
12957     else // store present elements to add them to a group
12958       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12959       {
12960         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12961       }
12962
12963   } // loop on given elements
12964
12965   // ---------------------------------------------
12966   // 4. Fill group with boundary elements
12967   // ---------------------------------------------
12968   if ( group )
12969   {
12970     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12971       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12972         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12973   }
12974   tgtEditor.myLastCreatedElems.clear();
12975   tgtEditor2.myLastCreatedElems.clear();
12976
12977   // -----------------------
12978   // 5. Copy given elements
12979   // -----------------------
12980   if ( toCopyElements && targetMesh != myMesh )
12981   {
12982     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12983     else                  eIt = SMESHUtils::elemSetIterator( elements );
12984     while (eIt->more())
12985     {
12986       const SMDS_MeshElement* elem = eIt->next();
12987       tgtNodes.resize( elem->NbNodes() );
12988       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12989         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12990       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12991
12992       tgtEditor.myLastCreatedElems.clear();
12993     }
12994   }
12995   return nbAddedBnd;
12996 }
12997
12998 //================================================================================
12999 /*!
13000  * \brief Copy node position and set \a to node on the same geometry
13001  */
13002 //================================================================================
13003
13004 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13005                                      const SMDS_MeshNode* to )
13006 {
13007   if ( !from || !to ) return;
13008
13009   SMDS_PositionPtr pos = from->GetPosition();
13010   if ( !pos || from->getshapeId() < 1 ) return;
13011
13012   switch ( pos->GetTypeOfPosition() )
13013   {
13014   case SMDS_TOP_3DSPACE: break;
13015
13016   case SMDS_TOP_FACE:
13017   {
13018     SMDS_FacePositionPtr fPos = pos;
13019     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13020                                 fPos->GetUParameter(), fPos->GetVParameter() );
13021     break;
13022   }
13023   case SMDS_TOP_EDGE:
13024   {
13025     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13026     SMDS_EdgePositionPtr ePos = pos;
13027     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13028     break;
13029   }
13030   case SMDS_TOP_VERTEX:
13031   {
13032     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13033     break;
13034   }
13035   case SMDS_TOP_UNSPEC:
13036   default:;
13037   }
13038 }