Salome HOME
db5b6507f416aed183e3e45ad6e09a673a5d6a5c
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 //=======================================================================
109 //function : SMESH_MeshEditor
110 //purpose  :
111 //=======================================================================
112
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114   :myMesh( theMesh ) // theMesh may be NULL
115 {
116 }
117
118 //================================================================================
119 /*!
120  * \brief Return mesh DS
121  */
122 //================================================================================
123
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 {
126   return myMesh->GetMeshDS();
127 }
128
129
130 //================================================================================
131 /*!
132  * \brief Clears myLastCreatedNodes and myLastCreatedElems
133  */
134 //================================================================================
135
136 void SMESH_MeshEditor::ClearLastCreated()
137 {
138   SMESHUtils::FreeVector( myLastCreatedElems );
139   SMESHUtils::FreeVector( myLastCreatedNodes );
140 }
141
142 //================================================================================
143 /*!
144  * \brief Initializes members by an existing element
145  *  \param [in] elem - the source element
146  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147  */
148 //================================================================================
149
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
152 {
153   if ( elem )
154   {
155     myType = elem->GetType();
156     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157     {
158       myIsPoly = elem->IsPoly();
159       if ( myIsPoly )
160       {
161         myIsQuad = elem->IsQuadratic();
162         if ( myType == SMDSAbs_Volume && !basicOnly )
163         {
164           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165           myPolyhedQuantities.swap( quant );
166         }
167       }
168     }
169     else if ( myType == SMDSAbs_Ball && !basicOnly )
170     {
171       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
172     }
173   }
174   return *this;
175 }
176
177 //=======================================================================
178 /*!
179  * \brief Add element
180  */
181 //=======================================================================
182
183 SMDS_MeshElement*
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185                              const ElemFeatures&                  features)
186 {
187   SMDS_MeshElement* e = 0;
188   int nbnode = node.size();
189   SMESHDS_Mesh* mesh = GetMeshDS();
190   const int ID = features.myID;
191
192   switch ( features.myType ) {
193   case SMDSAbs_Face:
194     if ( !features.myIsPoly ) {
195       if      (nbnode == 3) {
196         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197         else           e = mesh->AddFace      (node[0], node[1], node[2] );
198       }
199       else if (nbnode == 4) {
200         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
202       }
203       else if (nbnode == 6) {
204         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205                                                node[4], node[5], ID);
206         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
207                                                node[4], node[5] );
208       }
209       else if (nbnode == 7) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6], ID);
212         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
213                                                node[4], node[5], node[6] );
214       }
215       else if (nbnode == 8) {
216         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7], ID);
218         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
219                                                node[4], node[5], node[6], node[7] );
220       }
221       else if (nbnode == 9) {
222         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8], ID);
224         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
225                                                node[4], node[5], node[6], node[7], node[8] );
226       }
227     }
228     else if ( !features.myIsQuad )
229     {
230       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231       else           e = mesh->AddPolygonalFace      (node    );
232     }
233     else if ( nbnode % 2 == 0 ) // just a protection
234     {
235       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236       else           e = mesh->AddQuadPolygonalFace      (node    );
237     }
238     break;
239
240   case SMDSAbs_Volume:
241     if ( !features.myIsPoly ) {
242       if      (nbnode == 4) {
243         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
245       }
246       else if (nbnode == 5) {
247         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248                                                  node[4], ID);
249         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
250                                                  node[4] );
251       }
252       else if (nbnode == 6) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], ID);
255         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
256                                                  node[4], node[5] );
257       }
258       else if (nbnode == 8) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7], ID);
261         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7] );
263       }
264       else if (nbnode == 10) {
265         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9] );
271       }
272       else if (nbnode == 12) {
273         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274                                                  node[4], node[5], node[6], node[7],
275                                                  node[8], node[9], node[10], node[11], ID);
276         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9], node[10], node[11] );
279       }
280       else if (nbnode == 13) {
281         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282                                                  node[4], node[5], node[6], node[7],
283                                                  node[8], node[9], node[10],node[11],
284                                                  node[12],ID);
285         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10],node[11],
288                                                  node[12] );
289       }
290       else if (nbnode == 15) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],node[13],node[14],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12],node[13],node[14] );
299       }
300       else if (nbnode == 20) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],node[15],
305                                                  node[16],node[17],node[18],node[19],ID);
306         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
307                                                  node[4], node[5], node[6], node[7],
308                                                  node[8], node[9], node[10],node[11],
309                                                  node[12],node[13],node[14],node[15],
310                                                  node[16],node[17],node[18],node[19] );
311       }
312       else if (nbnode == 27) {
313         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314                                                  node[4], node[5], node[6], node[7],
315                                                  node[8], node[9], node[10],node[11],
316                                                  node[12],node[13],node[14],node[15],
317                                                  node[16],node[17],node[18],node[19],
318                                                  node[20],node[21],node[22],node[23],
319                                                  node[24],node[25],node[26], ID);
320         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
321                                                  node[4], node[5], node[6], node[7],
322                                                  node[8], node[9], node[10],node[11],
323                                                  node[12],node[13],node[14],node[15],
324                                                  node[16],node[17],node[18],node[19],
325                                                  node[20],node[21],node[22],node[23],
326                                                  node[24],node[25],node[26] );
327       }
328     }
329     else if ( !features.myIsQuad )
330     {
331       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
333     }
334     else
335     {
336       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
338     }
339     break;
340
341   case SMDSAbs_Edge:
342     if ( nbnode == 2 ) {
343       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344       else           e = mesh->AddEdge      (node[0], node[1] );
345     }
346     else if ( nbnode == 3 ) {
347       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
349     }
350     break;
351
352   case SMDSAbs_0DElement:
353     if ( nbnode == 1 ) {
354       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355       else           e = mesh->Add0DElement      (node[0] );
356     }
357     break;
358
359   case SMDSAbs_Node:
360     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
362     break;
363
364   case SMDSAbs_Ball:
365     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
367     break;
368
369   default:;
370   }
371   if ( e ) myLastCreatedElems.push_back( e );
372   return e;
373 }
374
375 //=======================================================================
376 /*!
377  * \brief Add element
378  */
379 //=======================================================================
380
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382                                                const ElemFeatures& features)
383 {
384   vector<const SMDS_MeshNode*> nodes;
385   nodes.reserve( nodeIDs.size() );
386   vector<int>::const_iterator id = nodeIDs.begin();
387   while ( id != nodeIDs.end() ) {
388     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389       nodes.push_back( node );
390     else
391       return 0;
392   }
393   return AddElement( nodes, features );
394 }
395
396 //=======================================================================
397 //function : Remove
398 //purpose  : Remove a node or an element.
399 //           Modify a compute state of sub-meshes which become empty
400 //=======================================================================
401
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403                               const bool         isNodes )
404 {
405   ClearLastCreated();
406
407   SMESHDS_Mesh* aMesh = GetMeshDS();
408   set< SMESH_subMesh *> smmap;
409
410   int removed = 0;
411   list<int>::const_iterator it = theIDs.begin();
412   for ( ; it != theIDs.end(); it++ ) {
413     const SMDS_MeshElement * elem;
414     if ( isNodes )
415       elem = aMesh->FindNode( *it );
416     else
417       elem = aMesh->FindElement( *it );
418     if ( !elem )
419       continue;
420
421     // Notify VERTEX sub-meshes about modification
422     if ( isNodes ) {
423       const SMDS_MeshNode* node = cast2Node( elem );
424       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425         if ( int aShapeID = node->getshapeId() )
426           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
427             smmap.insert( sm );
428     }
429     // Find sub-meshes to notify about modification
430     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431     //     while ( nodeIt->more() ) {
432     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
434     //       if ( aPosition.get() ) {
435     //         if ( int aShapeID = aPosition->GetShapeId() ) {
436     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437     //             smmap.insert( sm );
438     //         }
439     //       }
440     //     }
441
442     // Do remove
443     if ( isNodes )
444       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445     else
446       aMesh->RemoveElement( elem );
447     removed++;
448   }
449
450   // Notify sub-meshes about modification
451   if ( !smmap.empty() ) {
452     set< SMESH_subMesh *>::iterator smIt;
453     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
455   }
456
457   //   // Check if the whole mesh becomes empty
458   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
460
461   return removed;
462 }
463
464 //================================================================================
465 /*!
466  * \brief Create 0D elements on all nodes of the given object.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  *  \param duplicateElements - to add one more 0D element to a node or not
471  */
472 //================================================================================
473
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475                                                    TIDSortedElemSet&       all0DElems,
476                                                    const bool              duplicateElements )
477 {
478   SMDS_ElemIteratorPtr elemIt;
479   if ( elements.empty() )
480   {
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482   }
483   else
484   {
485     elemIt = SMESHUtils::elemSetIterator( elements );
486   }
487
488   while ( elemIt->more() )
489   {
490     const SMDS_MeshElement* e = elemIt->next();
491     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492     while ( nodeIt->more() )
493     {
494       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496       if ( duplicateElements || !it0D->more() )
497       {
498         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499         all0DElems.insert( myLastCreatedElems.back() );
500       }
501       while ( it0D->more() )
502         all0DElems.insert( it0D->next() );
503     }
504   }
505 }
506
507 //=======================================================================
508 //function : FindShape
509 //purpose  : Return an index of the shape theElem is on
510 //           or zero if a shape not found
511 //=======================================================================
512
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
514 {
515   ClearLastCreated();
516
517   SMESHDS_Mesh * aMesh = GetMeshDS();
518   if ( aMesh->ShapeToMesh().IsNull() )
519     return 0;
520
521   int aShapeID = theElem->getshapeId();
522   if ( aShapeID < 1 )
523     return 0;
524
525   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526     if ( sm->Contains( theElem ))
527       return aShapeID;
528
529   if ( theElem->GetType() == SMDSAbs_Node ) {
530     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
531   }
532   else {
533     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
534   }
535
536   TopoDS_Shape aShape; // the shape a node of theElem is on
537   if ( theElem->GetType() != SMDSAbs_Node )
538   {
539     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540     while ( nodeIt->more() ) {
541       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542       if ((aShapeID = node->getshapeId()) > 0) {
543         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544           if ( sm->Contains( theElem ))
545             return aShapeID;
546           if ( aShape.IsNull() )
547             aShape = aMesh->IndexToShape( aShapeID );
548         }
549       }
550     }
551   }
552
553   // None of nodes is on a proper shape,
554   // find the shape among ancestors of aShape on which a node is
555   if ( !aShape.IsNull() ) {
556     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557     for ( ; ancIt.More(); ancIt.Next() ) {
558       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559       if ( sm && sm->Contains( theElem ))
560         return aMesh->ShapeToIndex( ancIt.Value() );
561     }
562   }
563   else
564   {
565     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566     while ( const SMESHDS_SubMesh* sm = smIt->next() )
567       if ( sm->Contains( theElem ))
568         return sm->GetID();
569   }
570
571   return 0;
572 }
573
574 //=======================================================================
575 //function : IsMedium
576 //purpose  :
577 //=======================================================================
578
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
580                                 const SMDSAbs_ElementType typeToCheck)
581 {
582   bool isMedium = false;
583   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584   while (it->more() && !isMedium ) {
585     const SMDS_MeshElement* elem = it->next();
586     isMedium = elem->IsMediumNode(node);
587   }
588   return isMedium;
589 }
590
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose  : Shift nodes in the array corresponded to quadratic triangle
594 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
596
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
598 {
599   const SMDS_MeshNode* nd1 = aNodes[0];
600   aNodes[0] = aNodes[1];
601   aNodes[1] = aNodes[2];
602   aNodes[2] = nd1;
603   const SMDS_MeshNode* nd2 = aNodes[3];
604   aNodes[3] = aNodes[4];
605   aNodes[4] = aNodes[5];
606   aNodes[5] = nd2;
607 }
608
609 //=======================================================================
610 //function : getNodesFromTwoTria
611 //purpose  : 
612 //=======================================================================
613
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615                                 const SMDS_MeshElement * theTria2,
616                                 vector< const SMDS_MeshNode*>& N1,
617                                 vector< const SMDS_MeshNode*>& N2)
618 {
619   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620   if ( N1.size() < 6 ) return false;
621   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622   if ( N2.size() < 6 ) return false;
623
624   int sames[3] = {-1,-1,-1};
625   int nbsames = 0;
626   int i, j;
627   for(i=0; i<3; i++) {
628     for(j=0; j<3; j++) {
629       if(N1[i]==N2[j]) {
630         sames[i] = j;
631         nbsames++;
632         break;
633       }
634     }
635   }
636   if(nbsames!=2) return false;
637   if(sames[0]>-1) {
638     shiftNodesQuadTria(N1);
639     if(sames[1]>-1) {
640       shiftNodesQuadTria(N1);
641     }
642   }
643   i = sames[0] + sames[1] + sames[2];
644   for(; i<2; i++) {
645     shiftNodesQuadTria(N2);
646   }
647   // now we receive following N1 and N2 (using numeration as in the image below)
648   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
649   // i.e. first nodes from both arrays form a new diagonal
650   return true;
651 }
652
653 //=======================================================================
654 //function : InverseDiag
655 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
656 //           but having other common link.
657 //           Return False if args are improper
658 //=======================================================================
659
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661                                     const SMDS_MeshElement * theTria2 )
662 {
663   ClearLastCreated();
664
665   if ( !theTria1 || !theTria2 ||
666        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668        theTria1->GetType() != SMDSAbs_Face ||
669        theTria2->GetType() != SMDSAbs_Face )
670     return false;
671
672   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673       (theTria2->GetEntityType() == SMDSEntity_Triangle))
674   {
675     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
677     //    |/ |                                         | \|
678     //  B +--+ 2                                     B +--+ 2
679
680     // put nodes in array and find out indices of the same ones
681     const SMDS_MeshNode* aNodes [6];
682     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
683     int i = 0;
684     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685     while ( it->more() ) {
686       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
687
688       if ( i > 2 ) // theTria2
689         // find same node of theTria1
690         for ( int j = 0; j < 3; j++ )
691           if ( aNodes[ i ] == aNodes[ j ]) {
692             sameInd[ j ] = i;
693             sameInd[ i ] = j;
694             break;
695           }
696       // next
697       i++;
698       if ( i == 3 ) {
699         if ( it->more() )
700           return false; // theTria1 is not a triangle
701         it = theTria2->nodesIterator();
702       }
703       if ( i == 6 && it->more() )
704         return false; // theTria2 is not a triangle
705     }
706
707     // find indices of 1,2 and of A,B in theTria1
708     int iA = -1, iB = 0, i1 = 0, i2 = 0;
709     for ( i = 0; i < 6; i++ ) {
710       if ( sameInd [ i ] == -1 ) {
711         if ( i < 3 ) i1 = i;
712         else         i2 = i;
713       }
714       else if (i < 3) {
715         if ( iA >= 0) iB = i;
716         else          iA = i;
717       }
718     }
719     // nodes 1 and 2 should not be the same
720     if ( aNodes[ i1 ] == aNodes[ i2 ] )
721       return false;
722
723     // theTria1: A->2
724     aNodes[ iA ] = aNodes[ i2 ];
725     // theTria2: B->1
726     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
727
728     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
730
731     return true;
732
733   } // end if(F1 && F2)
734
735   // check case of quadratic faces
736   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
738     return false;
739   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
741     return false;
742
743   //       5
744   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
746   //    |   / |
747   //  7 +  +  + 6
748   //    | /9  |
749   //    |/    |
750   //  4 +--+--+ 3
751   //       8
752
753   vector< const SMDS_MeshNode* > N1;
754   vector< const SMDS_MeshNode* > N2;
755   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
756     return false;
757   // now we receive following N1 and N2 (using numeration as above image)
758   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
759   // i.e. first nodes from both arrays determ new diagonal
760
761   vector< const SMDS_MeshNode*> N1new( N1.size() );
762   vector< const SMDS_MeshNode*> N2new( N2.size() );
763   N1new.back() = N1.back(); // central node of biquadratic
764   N2new.back() = N2.back();
765   N1new[0] = N1[0];  N2new[0] = N1[0];
766   N1new[1] = N2[0];  N2new[1] = N1[1];
767   N1new[2] = N2[1];  N2new[2] = N2[0];
768   N1new[3] = N1[4];  N2new[3] = N1[3];
769   N1new[4] = N2[3];  N2new[4] = N2[5];
770   N1new[5] = N1[5];  N2new[5] = N1[4];
771   // change nodes in faces
772   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
774
775   // move the central node of biquadratic triangle
776   SMESH_MesherHelper helper( *GetMesh() );
777   for ( int is2nd = 0; is2nd < 2; ++is2nd )
778   {
779     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
780     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781     if ( nodes.size() < 7 )
782       continue;
783     helper.SetSubShape( tria->getshapeId() );
784     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
785     gp_Pnt xyz;
786     if ( F.IsNull() )
787     {
788       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789               SMESH_NodeXYZ( nodes[4] ) +
790               SMESH_NodeXYZ( nodes[5] )) / 3.;
791     }
792     else
793     {
794       bool checkUV;
795       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
798       TopLoc_Location loc;
799       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800       xyz = S->Value( uv.X(), uv.Y() );
801       xyz.Transform( loc );
802       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
803            nodes[6]->getshapeId() > 0 )
804         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
805     }
806     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
807   }
808   return true;
809 }
810
811 //=======================================================================
812 //function : findTriangles
813 //purpose  : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
815
816 static bool findTriangles(const SMDS_MeshNode *    theNode1,
817                           const SMDS_MeshNode *    theNode2,
818                           const SMDS_MeshElement*& theTria1,
819                           const SMDS_MeshElement*& theTria2)
820 {
821   if ( !theNode1 || !theNode2 ) return false;
822
823   theTria1 = theTria2 = 0;
824
825   set< const SMDS_MeshElement* > emap;
826   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
827   while (it->more()) {
828     const SMDS_MeshElement* elem = it->next();
829     if ( elem->NbCornerNodes() == 3 )
830       emap.insert( elem );
831   }
832   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
833   while (it->more()) {
834     const SMDS_MeshElement* elem = it->next();
835     if ( emap.count( elem )) {
836       if ( !theTria1 )
837       {
838         theTria1 = elem;
839       }
840       else  
841       {
842         theTria2 = elem;
843         // theTria1 must be element with minimum ID
844         if ( theTria2->GetID() < theTria1->GetID() )
845           std::swap( theTria2, theTria1 );
846         return true;
847       }
848     }
849   }
850   return false;
851 }
852
853 //=======================================================================
854 //function : InverseDiag
855 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
856 //           with ones built on the same 4 nodes but having other common link.
857 //           Return false if proper faces not found
858 //=======================================================================
859
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861                                     const SMDS_MeshNode * theNode2)
862 {
863   ClearLastCreated();
864
865   const SMDS_MeshElement *tr1, *tr2;
866   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
867     return false;
868
869   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
871     return false;
872
873   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
875
876     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
878     //    |/ |                                    | \|
879     //  B +--+ 2                                B +--+ 2
880
881     // put nodes in array
882     // and find indices of 1,2 and of A in tr1 and of B in tr2
883     int i, iA1 = 0, i1 = 0;
884     const SMDS_MeshNode* aNodes1 [3];
885     SMDS_ElemIteratorPtr it;
886     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888       if ( aNodes1[ i ] == theNode1 )
889         iA1 = i; // node A in tr1
890       else if ( aNodes1[ i ] != theNode2 )
891         i1 = i;  // node 1
892     }
893     int iB2 = 0, i2 = 0;
894     const SMDS_MeshNode* aNodes2 [3];
895     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897       if ( aNodes2[ i ] == theNode2 )
898         iB2 = i; // node B in tr2
899       else if ( aNodes2[ i ] != theNode1 )
900         i2 = i;  // node 2
901     }
902
903     // nodes 1 and 2 should not be the same
904     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
905       return false;
906
907     // tr1: A->2
908     aNodes1[ iA1 ] = aNodes2[ i2 ];
909     // tr2: B->1
910     aNodes2[ iB2 ] = aNodes1[ i1 ];
911
912     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
914
915     return true;
916   }
917
918   // check case of quadratic faces
919   return InverseDiag(tr1,tr2);
920 }
921
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
925 //           fusion of triangles tr1 and tr2 having shared link on
926 //           theNode1 and theNode2
927 //=======================================================================
928
929 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
930                         const SMDS_MeshNode *    theNode1,
931                         const SMDS_MeshNode *    theNode2,
932                         const SMDS_MeshElement * tr1,
933                         const SMDS_MeshElement * tr2 )
934 {
935   if( tr1->NbNodes() != tr2->NbNodes() )
936     return false;
937   // find the 4-th node to insert into tr1
938   const SMDS_MeshNode* n4 = 0;
939   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
940   int i=0;
941   while ( !n4 && i<3 ) {
942     const SMDS_MeshNode * n = cast2Node( it->next() );
943     i++;
944     bool isDiag = ( n == theNode1 || n == theNode2 );
945     if ( !isDiag )
946       n4 = n;
947   }
948   // Make an array of nodes to be in a quadrangle
949   int iNode = 0, iFirstDiag = -1;
950   it = tr1->nodesIterator();
951   i=0;
952   while ( i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( isDiag ) {
957       if ( iFirstDiag < 0 )
958         iFirstDiag = iNode;
959       else if ( iNode - iFirstDiag == 1 )
960         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
961     }
962     else if ( n == n4 ) {
963       return false; // tr1 and tr2 should not have all the same nodes
964     }
965     theQuadNodes[ iNode++ ] = n;
966   }
967   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968     theQuadNodes[ iNode ] = n4;
969
970   return true;
971 }
972
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
976 //           with a quadrangle built on the same 4 nodes.
977 //           Return false if proper faces not found
978 //=======================================================================
979
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981                                    const SMDS_MeshNode * theNode2)
982 {
983   ClearLastCreated();
984
985   const SMDS_MeshElement *tr1, *tr2;
986   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
987     return false;
988
989   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
991     return false;
992
993   SMESHDS_Mesh * aMesh = GetMeshDS();
994
995   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996       (tr2->GetEntityType() == SMDSEntity_Triangle))
997   {
998     const SMDS_MeshNode* aNodes [ 4 ];
999     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1000       return false;
1001
1002     const SMDS_MeshElement* newElem = 0;
1003     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004     myLastCreatedElems.push_back(newElem);
1005     AddToSameGroups( newElem, tr1, aMesh );
1006     int aShapeId = tr1->getshapeId();
1007     if ( aShapeId )
1008       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009
1010     aMesh->RemoveElement( tr1 );
1011     aMesh->RemoveElement( tr2 );
1012
1013     return true;
1014   }
1015
1016   // check case of quadratic faces
1017   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1018     return false;
1019   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1020     return false;
1021
1022   //       5
1023   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1025   //    |   / |
1026   //  7 +  +  + 6
1027   //    | /9  |
1028   //    |/    |
1029   //  4 +--+--+ 3
1030   //       8
1031
1032   vector< const SMDS_MeshNode* > N1;
1033   vector< const SMDS_MeshNode* > N2;
1034   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1035     return false;
1036   // now we receive following N1 and N2 (using numeration as above image)
1037   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1038   // i.e. first nodes from both arrays determ new diagonal
1039
1040   const SMDS_MeshNode* aNodes[8];
1041   aNodes[0] = N1[0];
1042   aNodes[1] = N1[1];
1043   aNodes[2] = N2[0];
1044   aNodes[3] = N2[1];
1045   aNodes[4] = N1[3];
1046   aNodes[5] = N2[5];
1047   aNodes[6] = N2[3];
1048   aNodes[7] = N1[5];
1049
1050   const SMDS_MeshElement* newElem = 0;
1051   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053   myLastCreatedElems.push_back(newElem);
1054   AddToSameGroups( newElem, tr1, aMesh );
1055   int aShapeId = tr1->getshapeId();
1056   if ( aShapeId )
1057   {
1058     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1059   }
1060   aMesh->RemoveElement( tr1 );
1061   aMesh->RemoveElement( tr2 );
1062
1063   // remove middle node (9)
1064   GetMeshDS()->RemoveNode( N1[4] );
1065
1066   return true;
1067 }
1068
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose  : Reverse theElement orientation
1072 //=======================================================================
1073
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1075 {
1076   ClearLastCreated();
1077
1078   if (!theElem)
1079     return false;
1080   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081   if ( !it || !it->more() )
1082     return false;
1083
1084   const SMDSAbs_ElementType type = theElem->GetType();
1085   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1086     return false;
1087
1088   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1090   {
1091     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1092     if (!aPolyedre) {
1093       MESSAGE("Warning: bad volumic element");
1094       return false;
1095     }
1096     SMDS_VolumeTool vTool( aPolyedre );
1097     const int nbFaces = vTool.NbFaces();
1098     vector<int> quantities( nbFaces );
1099     vector<const SMDS_MeshNode *> poly_nodes;
1100
1101     // check if all facets are oriented equally
1102     bool sameOri = true;
1103     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1104     for (int iface = 0; iface < nbFaces; iface++)
1105     {
1106       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1107       if ( facetOri[ iface ] != facetOri[ 0 ])
1108         sameOri = false;
1109     }
1110
1111     // reverse faces of the polyhedron
1112     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1113     poly_nodes.reserve( vTool.NbNodes() );
1114     for ( int iface = 0; iface < nbFaces; iface++ )
1115     {
1116       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1117       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1118       bool toReverse = ( facetOri[ iface ] != neededOri );
1119
1120       quantities[ iface ] = nbFaceNodes;
1121
1122       if ( toReverse )
1123         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1124           poly_nodes.push_back( nodes[ inode ]);
1125       else
1126         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1127     }
1128     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1129   }
1130   else // other elements
1131   {
1132     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1133     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1134     if ( interlace.empty() )
1135     {
1136       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1137     }
1138     else
1139     {
1140       SMDS_MeshCell::applyInterlace( interlace, nodes );
1141     }
1142     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1143   }
1144   return false;
1145 }
1146
1147 //================================================================================
1148 /*!
1149  * \brief Reorient faces.
1150  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1151  * \param theDirection - desired direction of normal of \a theFace
1152  * \param theFace - one of \a theFaces that should be oriented according to
1153  *        \a theDirection and whose orientation defines orientation of other faces
1154  * \return number of reoriented faces.
1155  */
1156 //================================================================================
1157
1158 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1159                                   const gp_Dir&            theDirection,
1160                                   const SMDS_MeshElement * theFace)
1161 {
1162   int nbReori = 0;
1163   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1164
1165   if ( theFaces.empty() )
1166   {
1167     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1168     while ( fIt->more() )
1169       theFaces.insert( theFaces.end(), fIt->next() );
1170   }
1171
1172   // orient theFace according to theDirection
1173   gp_XYZ normal;
1174   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1175   if ( normal * theDirection.XYZ() < 0 )
1176     nbReori += Reorient( theFace );
1177
1178   // Orient other faces
1179
1180   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1181   TIDSortedElemSet avoidSet;
1182   set< SMESH_TLink > checkedLinks;
1183   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1184
1185   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1186     theFaces.erase( theFace );
1187   startFaces.insert( theFace );
1188
1189   int nodeInd1, nodeInd2;
1190   const SMDS_MeshElement*           otherFace;
1191   vector< const SMDS_MeshElement* > facesNearLink;
1192   vector< std::pair< int, int > >   nodeIndsOfFace;
1193
1194   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1195   while ( !startFaces.empty() )
1196   {
1197     startFace = startFaces.begin();
1198     theFace = *startFace;
1199     startFaces.erase( startFace );
1200     if ( !visitedFaces.insert( theFace ).second )
1201       continue;
1202
1203     avoidSet.clear();
1204     avoidSet.insert(theFace);
1205
1206     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1207
1208     const int nbNodes = theFace->NbCornerNodes();
1209     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1210     {
1211       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1212       linkIt_isNew = checkedLinks.insert( link );
1213       if ( !linkIt_isNew.second )
1214       {
1215         // link has already been checked and won't be encountered more
1216         // if the group (theFaces) is manifold
1217         //checkedLinks.erase( linkIt_isNew.first );
1218       }
1219       else
1220       {
1221         facesNearLink.clear();
1222         nodeIndsOfFace.clear();
1223         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1224                                                              theFaces, avoidSet,
1225                                                              &nodeInd1, &nodeInd2 )))
1226           if ( otherFace != theFace)
1227           {
1228             facesNearLink.push_back( otherFace );
1229             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1230             avoidSet.insert( otherFace );
1231           }
1232         if ( facesNearLink.size() > 1 )
1233         {
1234           // NON-MANIFOLD mesh shell !
1235           // select a face most co-directed with theFace,
1236           // other faces won't be visited this time
1237           gp_XYZ NF, NOF;
1238           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1239           double proj, maxProj = -1;
1240           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1241             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1242             if (( proj = Abs( NF * NOF )) > maxProj ) {
1243               maxProj = proj;
1244               otherFace = facesNearLink[i];
1245               nodeInd1  = nodeIndsOfFace[i].first;
1246               nodeInd2  = nodeIndsOfFace[i].second;
1247             }
1248           }
1249           // not to visit rejected faces
1250           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1251             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1252               visitedFaces.insert( facesNearLink[i] );
1253         }
1254         else if ( facesNearLink.size() == 1 )
1255         {
1256           otherFace = facesNearLink[0];
1257           nodeInd1  = nodeIndsOfFace.back().first;
1258           nodeInd2  = nodeIndsOfFace.back().second;
1259         }
1260         if ( otherFace && otherFace != theFace)
1261         {
1262           // link must be reverse in otherFace if orientation to otherFace
1263           // is same as that of theFace
1264           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1265           {
1266             nbReori += Reorient( otherFace );
1267           }
1268           startFaces.insert( otherFace );
1269         }
1270       }
1271       std::swap( link.first, link.second ); // reverse the link
1272     }
1273   }
1274   return nbReori;
1275 }
1276
1277 //================================================================================
1278 /*!
1279  * \brief Reorient faces basing on orientation of adjacent volumes.
1280  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1281  * \param theVolumes - reference volumes.
1282  * \param theOutsideNormal - to orient faces to have their normal
1283  *        pointing either \a outside or \a inside the adjacent volumes.
1284  * \return number of reoriented faces.
1285  */
1286 //================================================================================
1287
1288 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1289                                       TIDSortedElemSet & theVolumes,
1290                                       const bool         theOutsideNormal)
1291 {
1292   int nbReori = 0;
1293
1294   SMDS_ElemIteratorPtr faceIt;
1295   if ( theFaces.empty() )
1296     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1297   else
1298     faceIt = SMESHUtils::elemSetIterator( theFaces );
1299
1300   vector< const SMDS_MeshNode* > faceNodes;
1301   TIDSortedElemSet checkedVolumes;
1302   set< const SMDS_MeshNode* > faceNodesSet;
1303   SMDS_VolumeTool volumeTool;
1304
1305   while ( faceIt->more() ) // loop on given faces
1306   {
1307     const SMDS_MeshElement* face = faceIt->next();
1308     if ( face->GetType() != SMDSAbs_Face )
1309       continue;
1310
1311     const size_t nbCornersNodes = face->NbCornerNodes();
1312     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1313
1314     checkedVolumes.clear();
1315     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1316     while ( vIt->more() )
1317     {
1318       const SMDS_MeshElement* volume = vIt->next();
1319
1320       if ( !checkedVolumes.insert( volume ).second )
1321         continue;
1322       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1323         continue;
1324
1325       // is volume adjacent?
1326       bool allNodesCommon = true;
1327       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1328         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1329       if ( !allNodesCommon )
1330         continue;
1331
1332       // get nodes of a corresponding volume facet
1333       faceNodesSet.clear();
1334       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1335       volumeTool.Set( volume );
1336       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1337       if ( facetID < 0 ) continue;
1338       volumeTool.SetExternalNormal();
1339       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1340
1341       // compare order of faceNodes and facetNodes
1342       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1343       int iNN[2];
1344       for ( int i = 0; i < 2; ++i )
1345       {
1346         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1347         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1348           if ( faceNodes[ iN ] == n )
1349           {
1350             iNN[ i ] = iN;
1351             break;
1352           }
1353       }
1354       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1355       if ( isOutside != theOutsideNormal )
1356         nbReori += Reorient( face );
1357     }
1358   }  // loop on given faces
1359
1360   return nbReori;
1361 }
1362
1363 //=======================================================================
1364 //function : getBadRate
1365 //purpose  :
1366 //=======================================================================
1367
1368 static double getBadRate (const SMDS_MeshElement*               theElem,
1369                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1370 {
1371   SMESH::Controls::TSequenceOfXYZ P;
1372   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1373     return 1e100;
1374   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1375   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1376 }
1377
1378 //=======================================================================
1379 //function : QuadToTri
1380 //purpose  : Cut quadrangles into triangles.
1381 //           theCrit is used to select a diagonal to cut
1382 //=======================================================================
1383
1384 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1385                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1386 {
1387   ClearLastCreated();
1388
1389   if ( !theCrit.get() )
1390     return false;
1391
1392   SMESHDS_Mesh *       aMesh = GetMeshDS();
1393   Handle(Geom_Surface) surface;
1394   SMESH_MesherHelper   helper( *GetMesh() );
1395
1396   myLastCreatedElems.reserve( theElems.size() * 2 );
1397
1398   TIDSortedElemSet::iterator itElem;
1399   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1400   {
1401     const SMDS_MeshElement* elem = *itElem;
1402     if ( !elem || elem->GetType() != SMDSAbs_Face )
1403       continue;
1404     if ( elem->NbCornerNodes() != 4 )
1405       continue;
1406
1407     // retrieve element nodes
1408     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1409
1410     // compare two sets of possible triangles
1411     double aBadRate1, aBadRate2; // to what extent a set is bad
1412     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1413     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1414     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1415
1416     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1417     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1418     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1419
1420     const int aShapeId = FindShape( elem );
1421     const SMDS_MeshElement* newElem1 = 0;
1422     const SMDS_MeshElement* newElem2 = 0;
1423
1424     if ( !elem->IsQuadratic() ) // split linear quadrangle
1425     {
1426       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1427       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1428       if ( aBadRate1 <= aBadRate2 ) {
1429         // tr1 + tr2 is better
1430         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1431         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1432       }
1433       else {
1434         // tr3 + tr4 is better
1435         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1436         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1437       }
1438     }
1439     else // split quadratic quadrangle
1440     {
1441       helper.SetIsQuadratic( true );
1442       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1443
1444       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1445       if ( aNodes.size() == 9 )
1446       {
1447         helper.SetIsBiQuadratic( true );
1448         if ( aBadRate1 <= aBadRate2 )
1449           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1450         else
1451           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1452       }
1453       // create a new element
1454       if ( aBadRate1 <= aBadRate2 ) {
1455         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1456         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1457       }
1458       else {
1459         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1460         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1461       }
1462     } // quadratic case
1463
1464     // care of a new element
1465
1466     myLastCreatedElems.push_back(newElem1);
1467     myLastCreatedElems.push_back(newElem2);
1468     AddToSameGroups( newElem1, elem, aMesh );
1469     AddToSameGroups( newElem2, elem, aMesh );
1470
1471     // put a new triangle on the same shape
1472     if ( aShapeId )
1473       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1474     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1475
1476     aMesh->RemoveElement( elem );
1477   }
1478   return true;
1479 }
1480
1481 //=======================================================================
1482 /*!
1483  * \brief Split each of given quadrangles into 4 triangles.
1484  * \param theElems - The faces to be split. If empty all faces are split.
1485  */
1486 //=======================================================================
1487
1488 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1489 {
1490   ClearLastCreated();
1491   myLastCreatedElems.reserve( theElems.size() * 4 );
1492
1493   SMESH_MesherHelper helper( *GetMesh() );
1494   helper.SetElementsOnShape( true );
1495
1496   SMDS_ElemIteratorPtr faceIt;
1497   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1498   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1499
1500   bool   checkUV;
1501   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1502   gp_XYZ xyz[9];
1503   vector< const SMDS_MeshNode* > nodes;
1504   SMESHDS_SubMesh*               subMeshDS = 0;
1505   TopoDS_Face                    F;
1506   Handle(Geom_Surface)           surface;
1507   TopLoc_Location                loc;
1508
1509   while ( faceIt->more() )
1510   {
1511     const SMDS_MeshElement* quad = faceIt->next();
1512     if ( !quad || quad->NbCornerNodes() != 4 )
1513       continue;
1514
1515     // get a surface the quad is on
1516
1517     if ( quad->getshapeId() < 1 )
1518     {
1519       F.Nullify();
1520       helper.SetSubShape( 0 );
1521       subMeshDS = 0;
1522     }
1523     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1524     {
1525       helper.SetSubShape( quad->getshapeId() );
1526       if ( !helper.GetSubShape().IsNull() &&
1527            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1528       {
1529         F = TopoDS::Face( helper.GetSubShape() );
1530         surface = BRep_Tool::Surface( F, loc );
1531         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1532       }
1533       else
1534       {
1535         helper.SetSubShape( 0 );
1536         subMeshDS = 0;
1537       }
1538     }
1539
1540     // create a central node
1541
1542     const SMDS_MeshNode* nCentral;
1543     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1544
1545     if ( nodes.size() == 9 )
1546     {
1547       nCentral = nodes.back();
1548     }
1549     else
1550     {
1551       size_t iN = 0;
1552       if ( F.IsNull() )
1553       {
1554         for ( ; iN < nodes.size(); ++iN )
1555           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1556
1557         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1558           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1559
1560         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1561                                    xyz[0], xyz[1], xyz[2], xyz[3],
1562                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1563       }
1564       else
1565       {
1566         for ( ; iN < nodes.size(); ++iN )
1567           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1568
1569         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1570           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1571
1572         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1573                                   uv[0], uv[1], uv[2], uv[3],
1574                                   uv[4], uv[5], uv[6], uv[7] );
1575
1576         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1577         xyz[ 8 ] = p.XYZ();
1578       }
1579
1580       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1581                                  uv[8].X(), uv[8].Y() );
1582       myLastCreatedNodes.push_back( nCentral );
1583     }
1584
1585     // create 4 triangles
1586
1587     helper.SetIsQuadratic  ( nodes.size() > 4 );
1588     helper.SetIsBiQuadratic( nodes.size() == 9 );
1589     if ( helper.GetIsQuadratic() )
1590       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1591
1592     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1593
1594     for ( int i = 0; i < 4; ++i )
1595     {
1596       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1597                                                nodes[(i+1)%4],
1598                                                nCentral );
1599       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1600       myLastCreatedElems.push_back( tria );
1601     }
1602   }
1603 }
1604
1605 //=======================================================================
1606 //function : BestSplit
1607 //purpose  : Find better diagonal for cutting.
1608 //=======================================================================
1609
1610 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1611                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1612 {
1613   ClearLastCreated();
1614
1615   if (!theCrit.get())
1616     return -1;
1617
1618   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1619     return -1;
1620
1621   if( theQuad->NbNodes()==4 ||
1622       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1623
1624     // retrieve element nodes
1625     const SMDS_MeshNode* aNodes [4];
1626     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1627     int i = 0;
1628     //while (itN->more())
1629     while (i<4) {
1630       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1631     }
1632     // compare two sets of possible triangles
1633     double aBadRate1, aBadRate2; // to what extent a set is bad
1634     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1635     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1636     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1637
1638     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1639     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1640     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1641     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1642     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1643     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1644       return 1; // diagonal 1-3
1645
1646     return 2; // diagonal 2-4
1647   }
1648   return -1;
1649 }
1650
1651 namespace
1652 {
1653   // Methods of splitting volumes into tetra
1654
1655   const int theHexTo5_1[5*4+1] =
1656     {
1657       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1658     };
1659   const int theHexTo5_2[5*4+1] =
1660     {
1661       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1662     };
1663   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1664
1665   const int theHexTo6_1[6*4+1] =
1666     {
1667       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1668     };
1669   const int theHexTo6_2[6*4+1] =
1670     {
1671       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1672     };
1673   const int theHexTo6_3[6*4+1] =
1674     {
1675       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1676     };
1677   const int theHexTo6_4[6*4+1] =
1678     {
1679       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1680     };
1681   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1682
1683   const int thePyraTo2_1[2*4+1] =
1684     {
1685       0, 1, 2, 4,    0, 2, 3, 4,   -1
1686     };
1687   const int thePyraTo2_2[2*4+1] =
1688     {
1689       1, 2, 3, 4,    1, 3, 0, 4,   -1
1690     };
1691   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1692
1693   const int thePentaTo3_1[3*4+1] =
1694     {
1695       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1696     };
1697   const int thePentaTo3_2[3*4+1] =
1698     {
1699       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1700     };
1701   const int thePentaTo3_3[3*4+1] =
1702     {
1703       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1704     };
1705   const int thePentaTo3_4[3*4+1] =
1706     {
1707       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1708     };
1709   const int thePentaTo3_5[3*4+1] =
1710     {
1711       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1712     };
1713   const int thePentaTo3_6[3*4+1] =
1714     {
1715       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1716     };
1717   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1718                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1719
1720   // Methods of splitting hexahedron into prisms
1721
1722   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1723     {
1724       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1725     };
1726   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1727     {
1728       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1729     };
1730   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1731     {
1732       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1733     };
1734
1735   const int theHexTo2Prisms_BT_1[6*2+1] =
1736     {
1737       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1738     };
1739   const int theHexTo2Prisms_BT_2[6*2+1] =
1740     {
1741       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1742     };
1743   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1744
1745   const int theHexTo2Prisms_LR_1[6*2+1] =
1746     {
1747       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1748     };
1749   const int theHexTo2Prisms_LR_2[6*2+1] =
1750     {
1751       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1752     };
1753   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1754
1755   const int theHexTo2Prisms_FB_1[6*2+1] =
1756     {
1757       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1758     };
1759   const int theHexTo2Prisms_FB_2[6*2+1] =
1760     {
1761       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1762     };
1763   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1764
1765
1766   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1767   {
1768     int _n1, _n2, _n3;
1769     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1770     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1771     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1772                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1773   };
1774   struct TSplitMethod
1775   {
1776     int        _nbSplits;
1777     int        _nbCorners;
1778     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1779     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1780     bool       _ownConn;      //!< to delete _connectivity in destructor
1781     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1782
1783     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1784       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1785     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1786     bool hasFacet( const TTriangleFacet& facet ) const
1787     {
1788       if ( _nbCorners == 4 )
1789       {
1790         const int* tetConn = _connectivity;
1791         for ( ; tetConn[0] >= 0; tetConn += 4 )
1792           if (( facet.contains( tetConn[0] ) +
1793                 facet.contains( tetConn[1] ) +
1794                 facet.contains( tetConn[2] ) +
1795                 facet.contains( tetConn[3] )) == 3 )
1796             return true;
1797       }
1798       else // prism, _nbCorners == 6
1799       {
1800         const int* prismConn = _connectivity;
1801         for ( ; prismConn[0] >= 0; prismConn += 6 )
1802         {
1803           if (( facet.contains( prismConn[0] ) &&
1804                 facet.contains( prismConn[1] ) &&
1805                 facet.contains( prismConn[2] ))
1806               ||
1807               ( facet.contains( prismConn[3] ) &&
1808                 facet.contains( prismConn[4] ) &&
1809                 facet.contains( prismConn[5] )))
1810             return true;
1811         }
1812       }
1813       return false;
1814     }
1815   };
1816
1817   //=======================================================================
1818   /*!
1819    * \brief return TSplitMethod for the given element to split into tetrahedra
1820    */
1821   //=======================================================================
1822
1823   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1824   {
1825     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1826
1827     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1828     // an edge and a face barycenter; tertaherdons are based on triangles and
1829     // a volume barycenter
1830     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1831
1832     // Find out how adjacent volumes are split
1833
1834     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1835     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1836     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1837     {
1838       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1839       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1840       if ( nbNodes < 4 ) continue;
1841
1842       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1843       const int* nInd = vol.GetFaceNodesIndices( iF );
1844       if ( nbNodes == 4 )
1845       {
1846         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1847         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1848         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1849         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1850       }
1851       else
1852       {
1853         int iCom = 0; // common node of triangle faces to split into
1854         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1855         {
1856           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1857                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1858                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1859           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1861                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1862           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1863           {
1864             triaSplits.push_back( t012 );
1865             triaSplits.push_back( t023 );
1866             break;
1867           }
1868         }
1869       }
1870       if ( !triaSplits.empty() )
1871         hasAdjacentSplits = true;
1872     }
1873
1874     // Among variants of split method select one compliant with adjacent volumes
1875
1876     TSplitMethod method;
1877     if ( !vol.Element()->IsPoly() && !is24TetMode )
1878     {
1879       int nbVariants = 2, nbTet = 0;
1880       const int** connVariants = 0;
1881       switch ( vol.Element()->GetEntityType() )
1882       {
1883       case SMDSEntity_Hexa:
1884       case SMDSEntity_Quad_Hexa:
1885       case SMDSEntity_TriQuad_Hexa:
1886         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1887           connVariants = theHexTo5, nbTet = 5;
1888         else
1889           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1890         break;
1891       case SMDSEntity_Pyramid:
1892       case SMDSEntity_Quad_Pyramid:
1893         connVariants = thePyraTo2;  nbTet = 2;
1894         break;
1895       case SMDSEntity_Penta:
1896       case SMDSEntity_Quad_Penta:
1897       case SMDSEntity_BiQuad_Penta:
1898         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1899         break;
1900       default:
1901         nbVariants = 0;
1902       }
1903       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1904       {
1905         // check method compliancy with adjacent tetras,
1906         // all found splits must be among facets of tetras described by this method
1907         method = TSplitMethod( nbTet, connVariants[variant] );
1908         if ( hasAdjacentSplits && method._nbSplits > 0 )
1909         {
1910           bool facetCreated = true;
1911           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1912           {
1913             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1914             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1915               facetCreated = method.hasFacet( *facet );
1916           }
1917           if ( !facetCreated )
1918             method = TSplitMethod(0); // incompatible method
1919         }
1920       }
1921     }
1922     if ( method._nbSplits < 1 )
1923     {
1924       // No standard method is applicable, use a generic solution:
1925       // each facet of a volume is split into triangles and
1926       // each of triangles and a volume barycenter form a tetrahedron.
1927
1928       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1929
1930       int* connectivity = new int[ maxTetConnSize + 1 ];
1931       method._connectivity = connectivity;
1932       method._ownConn = true;
1933       method._baryNode = !isHex27; // to create central node or not
1934
1935       int connSize = 0;
1936       int baryCenInd = vol.NbNodes() - int( isHex27 );
1937       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1938       {
1939         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1940         const int*   nInd = vol.GetFaceNodesIndices( iF );
1941         // find common node of triangle facets of tetra to create
1942         int iCommon = 0; // index in linear numeration
1943         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1944         if ( !triaSplits.empty() )
1945         {
1946           // by found facets
1947           const TTriangleFacet* facet = &triaSplits.front();
1948           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1949             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1950                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1951               break;
1952         }
1953         else if ( nbNodes > 3 && !is24TetMode )
1954         {
1955           // find the best method of splitting into triangles by aspect ratio
1956           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1957           map< double, int > badness2iCommon;
1958           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1959           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1960           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1961           {
1962             double badness = 0;
1963             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1964             {
1965               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1966                                       nodes[ iQ*((iLast-1)%nbNodes)],
1967                                       nodes[ iQ*((iLast  )%nbNodes)]);
1968               badness += getBadRate( &tria, aspectRatio );
1969             }
1970             badness2iCommon.insert( make_pair( badness, iCommon ));
1971           }
1972           // use iCommon with lowest badness
1973           iCommon = badness2iCommon.begin()->second;
1974         }
1975         if ( iCommon >= nbNodes )
1976           iCommon = 0; // something wrong
1977
1978         // fill connectivity of tetrahedra based on a current face
1979         int nbTet = nbNodes - 2;
1980         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1981         {
1982           int faceBaryCenInd;
1983           if ( isHex27 )
1984           {
1985             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1986             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1987           }
1988           else
1989           {
1990             method._faceBaryNode[ iF ] = 0;
1991             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1992           }
1993           nbTet = nbNodes;
1994           for ( int i = 0; i < nbTet; ++i )
1995           {
1996             int i1 = i, i2 = (i+1) % nbNodes;
1997             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1998             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1999             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2000             connectivity[ connSize++ ] = faceBaryCenInd;
2001             connectivity[ connSize++ ] = baryCenInd;
2002           }
2003         }
2004         else
2005         {
2006           for ( int i = 0; i < nbTet; ++i )
2007           {
2008             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2009             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2010             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2011             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2012             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2013             connectivity[ connSize++ ] = baryCenInd;
2014           }
2015         }
2016         method._nbSplits += nbTet;
2017
2018       } // loop on volume faces
2019
2020       connectivity[ connSize++ ] = -1;
2021
2022     } // end of generic solution
2023
2024     return method;
2025   }
2026   //=======================================================================
2027   /*!
2028    * \brief return TSplitMethod to split haxhedron into prisms
2029    */
2030   //=======================================================================
2031
2032   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2033                                     const int        methodFlags,
2034                                     const int        facetToSplit)
2035   {
2036     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2037     // B, T, L, B, R, F
2038     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2039
2040     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2041     {
2042       static TSplitMethod to4methods[4]; // order BT, LR, FB
2043       if ( to4methods[iF]._nbSplits == 0 )
2044       {
2045         switch ( iF ) {
2046         case 0:
2047           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2048           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2049           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2050           break;
2051         case 1:
2052           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2053           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2054           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2055           break;
2056         case 2:
2057           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2058           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2059           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2060           break;
2061         default: return to4methods[3];
2062         }
2063         to4methods[iF]._nbSplits  = 4;
2064         to4methods[iF]._nbCorners = 6;
2065       }
2066       return to4methods[iF];
2067     }
2068     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2069
2070     TSplitMethod method;
2071
2072     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2073
2074     const int nbVariants = 2, nbSplits = 2;
2075     const int** connVariants = 0;
2076     switch ( iF ) {
2077     case 0: connVariants = theHexTo2Prisms_BT; break;
2078     case 1: connVariants = theHexTo2Prisms_LR; break;
2079     case 2: connVariants = theHexTo2Prisms_FB; break;
2080     default: return method;
2081     }
2082
2083     // look for prisms adjacent via facetToSplit and an opposite one
2084     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2085     {
2086       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2087       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2088       if ( nbNodes != 4 ) return method;
2089
2090       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2091       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2092       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2093       TTriangleFacet* t;
2094       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2095         t = &t012;
2096       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097         t = &t123;
2098       else
2099         continue;
2100
2101       // there are adjacent prism
2102       for ( int variant = 0; variant < nbVariants; ++variant )
2103       {
2104         // check method compliancy with adjacent prisms,
2105         // the found prism facets must be among facets of prisms described by current method
2106         method._nbSplits     = nbSplits;
2107         method._nbCorners    = 6;
2108         method._connectivity = connVariants[ variant ];
2109         if ( method.hasFacet( *t ))
2110           return method;
2111       }
2112     }
2113
2114     // No adjacent prisms. Select a variant with a best aspect ratio.
2115
2116     double badness[2] = { 0., 0. };
2117     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2118     const SMDS_MeshNode** nodes = vol.GetNodes();
2119     for ( int variant = 0; variant < nbVariants; ++variant )
2120       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2121       {
2122         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2123         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2124
2125         method._connectivity = connVariants[ variant ];
2126         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2127         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2128         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2129
2130         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2131                                 nodes[ t->_n2 ],
2132                                 nodes[ t->_n3 ] );
2133         badness[ variant ] += getBadRate( &tria, aspectRatio );
2134       }
2135     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2136
2137     method._nbSplits     = nbSplits;
2138     method._nbCorners    = 6;
2139     method._connectivity = connVariants[ iBetter ];
2140
2141     return method;
2142   }
2143
2144   //================================================================================
2145   /*!
2146    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2147    */
2148   //================================================================================
2149
2150   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2151                                        const SMDSAbs_GeometryType geom ) const
2152   {
2153     // find the tetrahedron including the three nodes of facet
2154     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2155     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2156     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2157     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2158     while ( volIt1->more() )
2159     {
2160       const SMDS_MeshElement* v = volIt1->next();
2161       if ( v->GetGeomType() != geom )
2162         continue;
2163       const int lastCornerInd = v->NbCornerNodes() - 1;
2164       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2165         continue; // medium node not allowed
2166       const int ind2 = v->GetNodeIndex( n2 );
2167       if ( ind2 < 0 || lastCornerInd < ind2 )
2168         continue;
2169       const int ind3 = v->GetNodeIndex( n3 );
2170       if ( ind3 < 0 || lastCornerInd < ind3 )
2171         continue;
2172       return true;
2173     }
2174     return false;
2175   }
2176
2177   //=======================================================================
2178   /*!
2179    * \brief A key of a face of volume
2180    */
2181   //=======================================================================
2182
2183   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2184   {
2185     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2186     {
2187       TIDSortedNodeSet sortedNodes;
2188       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2189       int nbNodes = vol.NbFaceNodes( iF );
2190       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2191       for ( int i = 0; i < nbNodes; i += iQ )
2192         sortedNodes.insert( fNodes[i] );
2193       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2194       first.first   = (*(n++))->GetID();
2195       first.second  = (*(n++))->GetID();
2196       second.first  = (*(n++))->GetID();
2197       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2198     }
2199   };
2200 } // namespace
2201
2202 //=======================================================================
2203 //function : SplitVolumes
2204 //purpose  : Split volume elements into tetrahedra or prisms.
2205 //           If facet ID < 0, element is split into tetrahedra,
2206 //           else a hexahedron is split into prisms so that the given facet is
2207 //           split into triangles
2208 //=======================================================================
2209
2210 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2211                                      const int            theMethodFlags)
2212 {
2213   SMDS_VolumeTool    volTool;
2214   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2215   fHelper.ToFixNodeParameters( true );
2216
2217   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2218   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2219
2220   SMESH_SequenceOfElemPtr newNodes, newElems;
2221
2222   // map face of volume to it's baricenrtic node
2223   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2224   double bc[3];
2225   vector<const SMDS_MeshElement* > splitVols;
2226
2227   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2228   for ( ; elem2facet != theElems.end(); ++elem2facet )
2229   {
2230     const SMDS_MeshElement* elem = elem2facet->first;
2231     const int       facetToSplit = elem2facet->second;
2232     if ( elem->GetType() != SMDSAbs_Volume )
2233       continue;
2234     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2235     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2236       continue;
2237
2238     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2239
2240     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2241                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2242                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2243     if ( splitMethod._nbSplits < 1 ) continue;
2244
2245     // find submesh to add new tetras to
2246     if ( !subMesh || !subMesh->Contains( elem ))
2247     {
2248       int shapeID = FindShape( elem );
2249       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2250       subMesh = GetMeshDS()->MeshElements( shapeID );
2251     }
2252     int iQ;
2253     if ( elem->IsQuadratic() )
2254     {
2255       iQ = 2;
2256       // add quadratic links to the helper
2257       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2258       {
2259         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2260         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2261         for ( int iN = 0; iN < nbN; iN += iQ )
2262           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2263       }
2264       helper.SetIsQuadratic( true );
2265     }
2266     else
2267     {
2268       iQ = 1;
2269       helper.SetIsQuadratic( false );
2270     }
2271     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2272                                         volTool.GetNodes() + elem->NbNodes() );
2273     helper.SetElementsOnShape( true );
2274     if ( splitMethod._baryNode )
2275     {
2276       // make a node at barycenter
2277       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2278       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2279       nodes.push_back( gcNode );
2280       newNodes.push_back( gcNode );
2281     }
2282     if ( !splitMethod._faceBaryNode.empty() )
2283     {
2284       // make or find baricentric nodes of faces
2285       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2286       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2287       {
2288         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2289           volFace2BaryNode.insert
2290           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2291         if ( !f_n->second )
2292         {
2293           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2294           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2295         }
2296         nodes.push_back( iF_n->second = f_n->second );
2297       }
2298     }
2299
2300     // make new volumes
2301     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2302     const int* volConn = splitMethod._connectivity;
2303     if ( splitMethod._nbCorners == 4 ) // tetra
2304       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2305         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2306                                                                nodes[ volConn[1] ],
2307                                                                nodes[ volConn[2] ],
2308                                                                nodes[ volConn[3] ]));
2309     else // prisms
2310       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2311         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2312                                                                nodes[ volConn[1] ],
2313                                                                nodes[ volConn[2] ],
2314                                                                nodes[ volConn[3] ],
2315                                                                nodes[ volConn[4] ],
2316                                                                nodes[ volConn[5] ]));
2317
2318     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2319
2320     // Split faces on sides of the split volume
2321
2322     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2323     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2324     {
2325       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2326       if ( nbNodes < 4 ) continue;
2327
2328       // find an existing face
2329       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2330                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2331       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2332                                                                        /*noMedium=*/false))
2333       {
2334         // make triangles
2335         helper.SetElementsOnShape( false );
2336         vector< const SMDS_MeshElement* > triangles;
2337
2338         // find submesh to add new triangles in
2339         if ( !fSubMesh || !fSubMesh->Contains( face ))
2340         {
2341           int shapeID = FindShape( face );
2342           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2343         }
2344         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2345         if ( iF_n != splitMethod._faceBaryNode.end() )
2346         {
2347           const SMDS_MeshNode *baryNode = iF_n->second;
2348           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2349           {
2350             const SMDS_MeshNode* n1 = fNodes[iN];
2351             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2352             const SMDS_MeshNode *n3 = baryNode;
2353             if ( !volTool.IsFaceExternal( iF ))
2354               swap( n2, n3 );
2355             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2356           }
2357           if ( fSubMesh ) // update position of the bary node on geometry
2358           {
2359             if ( subMesh )
2360               subMesh->RemoveNode( baryNode );
2361             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2362             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2363             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2364             {
2365               fHelper.SetSubShape( s );
2366               gp_XY uv( 1e100, 1e100 );
2367               double distXYZ[4];
2368               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2369                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2370                    uv.X() < 1e100 )
2371               {
2372                 // node is too far from the surface
2373                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2374                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2375                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2376               }
2377             }
2378           }
2379         }
2380         else
2381         {
2382           // among possible triangles create ones described by split method
2383           const int* nInd = volTool.GetFaceNodesIndices( iF );
2384           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2385           int iCom = 0; // common node of triangle faces to split into
2386           list< TTriangleFacet > facets;
2387           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2388           {
2389             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2390                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2391                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2392             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2393                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2394                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2395             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2396             {
2397               facets.push_back( t012 );
2398               facets.push_back( t023 );
2399               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2400                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2401                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2402                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2403               break;
2404             }
2405           }
2406           list< TTriangleFacet >::iterator facet = facets.begin();
2407           if ( facet == facets.end() )
2408             break;
2409           for ( ; facet != facets.end(); ++facet )
2410           {
2411             if ( !volTool.IsFaceExternal( iF ))
2412               swap( facet->_n2, facet->_n3 );
2413             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2414                                                  volNodes[ facet->_n2 ],
2415                                                  volNodes[ facet->_n3 ]));
2416           }
2417         }
2418         for ( size_t i = 0; i < triangles.size(); ++i )
2419         {
2420           if ( !triangles[ i ]) continue;
2421           if ( fSubMesh )
2422             fSubMesh->AddElement( triangles[ i ]);
2423           newElems.push_back( triangles[ i ]);
2424         }
2425         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2426         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2427
2428       } // while a face based on facet nodes exists
2429     } // loop on volume faces to split them into triangles
2430
2431     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2432
2433     if ( geomType == SMDSEntity_TriQuad_Hexa )
2434     {
2435       // remove medium nodes that could become free
2436       for ( int i = 20; i < volTool.NbNodes(); ++i )
2437         if ( volNodes[i]->NbInverseElements() == 0 )
2438           GetMeshDS()->RemoveNode( volNodes[i] );
2439     }
2440   } // loop on volumes to split
2441
2442   myLastCreatedNodes = newNodes;
2443   myLastCreatedElems = newElems;
2444 }
2445
2446 //=======================================================================
2447 //function : GetHexaFacetsToSplit
2448 //purpose  : For hexahedra that will be split into prisms, finds facets to
2449 //           split into triangles. Only hexahedra adjacent to the one closest
2450 //           to theFacetNormal.Location() are returned.
2451 //param [in,out] theHexas - the hexahedra
2452 //param [in]     theFacetNormal - facet normal
2453 //param [out]    theFacets - the hexahedra and found facet IDs
2454 //=======================================================================
2455
2456 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2457                                              const gp_Ax1&     theFacetNormal,
2458                                              TFacetOfElem &    theFacets)
2459 {
2460 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2461
2462   // Find a hexa closest to the location of theFacetNormal
2463
2464   const SMDS_MeshElement* startHex;
2465   {
2466     // get SMDS_ElemIteratorPtr on theHexas
2467     typedef const SMDS_MeshElement*                                      TValue;
2468     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2469     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2470     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2471     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2472     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2473       ( new TElemSetIter( theHexas.begin(),
2474                           theHexas.end(),
2475                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2476
2477     SMESH_ElementSearcher* searcher =
2478       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2479
2480     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2481
2482     delete searcher;
2483
2484     if ( !startHex )
2485       throw SALOME_Exception( THIS_METHOD "startHex not found");
2486   }
2487
2488   // Select a facet of startHex by theFacetNormal
2489
2490   SMDS_VolumeTool vTool( startHex );
2491   double norm[3], dot, maxDot = 0;
2492   int facetID = -1;
2493   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2494     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2495     {
2496       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2497       if ( dot > maxDot )
2498       {
2499         facetID = iF;
2500         maxDot = dot;
2501       }
2502     }
2503   if ( facetID < 0 )
2504     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2505
2506   // Fill theFacets starting from facetID of startHex
2507
2508   // facets used for searching of volumes adjacent to already treated ones
2509   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2510   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2511   TFacetMap facetsToCheck;
2512
2513   set<const SMDS_MeshNode*> facetNodes;
2514   const SMDS_MeshElement*   curHex;
2515
2516   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2517
2518   while ( startHex )
2519   {
2520     // move in two directions from startHex via facetID
2521     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2522     {
2523       curHex       = startHex;
2524       int curFacet = facetID;
2525       if ( is2nd ) // do not treat startHex twice
2526       {
2527         vTool.Set( curHex );
2528         if ( vTool.IsFreeFace( curFacet, &curHex ))
2529         {
2530           curHex = 0;
2531         }
2532         else
2533         {
2534           vTool.GetFaceNodes( curFacet, facetNodes );
2535           vTool.Set( curHex );
2536           curFacet = vTool.GetFaceIndex( facetNodes );
2537         }
2538       }
2539       while ( curHex )
2540       {
2541         // store a facet to split
2542         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2543         {
2544           theFacets.insert( make_pair( curHex, -1 ));
2545           break;
2546         }
2547         if ( !allHex && !theHexas.count( curHex ))
2548           break;
2549
2550         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2551           theFacets.insert( make_pair( curHex, curFacet ));
2552         if ( !facetIt2isNew.second )
2553           break;
2554
2555         // remember not-to-split facets in facetsToCheck
2556         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2557         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2558         {
2559           if ( iF == curFacet && iF == oppFacet )
2560             continue;
2561           TVolumeFaceKey facetKey ( vTool, iF );
2562           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2563           pair< TFacetMap::iterator, bool > it2isnew =
2564             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2565           if ( !it2isnew.second )
2566             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2567         }
2568         // pass to a volume adjacent via oppFacet
2569         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2570         {
2571           curHex = 0;
2572         }
2573         else
2574         {
2575           // get a new curFacet
2576           vTool.GetFaceNodes( oppFacet, facetNodes );
2577           vTool.Set( curHex );
2578           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2579         }
2580       }
2581     } // move in two directions from startHex via facetID
2582
2583     // Find a new startHex by facetsToCheck
2584
2585     startHex = 0;
2586     facetID  = -1;
2587     TFacetMap::iterator fIt = facetsToCheck.begin();
2588     while ( !startHex && fIt != facetsToCheck.end() )
2589     {
2590       const TElemFacets&  elemFacets = fIt->second;
2591       const SMDS_MeshElement*    hex = elemFacets.first->first;
2592       int                 splitFacet = elemFacets.first->second;
2593       int               lateralFacet = elemFacets.second;
2594       facetsToCheck.erase( fIt );
2595       fIt = facetsToCheck.begin();
2596
2597       vTool.Set( hex );
2598       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2599            curHex->GetGeomType() != SMDSGeom_HEXA )
2600         continue;
2601       if ( !allHex && !theHexas.count( curHex ))
2602         continue;
2603
2604       startHex = curHex;
2605
2606       // find a facet of startHex to split
2607
2608       set<const SMDS_MeshNode*> lateralNodes;
2609       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2610       vTool.GetFaceNodes( splitFacet,   facetNodes );
2611       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2612       vTool.Set( startHex );
2613       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2614
2615       // look for a facet of startHex having common nodes with facetNodes
2616       // but not lateralFacet
2617       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2618       {
2619         if ( iF == lateralFacet )
2620           continue;
2621         int nbCommonNodes = 0;
2622         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2623         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2624           nbCommonNodes += facetNodes.count( nn[ iN ]);
2625
2626         if ( nbCommonNodes >= 2 )
2627         {
2628           facetID = iF;
2629           break;
2630         }
2631       }
2632       if ( facetID < 0 )
2633         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2634     }
2635   } //   while ( startHex )
2636
2637   return;
2638 }
2639
2640 namespace
2641 {
2642   //================================================================================
2643   /*!
2644    * \brief Selects nodes of several elements according to a given interlace
2645    *  \param [in] srcNodes - nodes to select from
2646    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2647    *  \param [in] interlace - indices of nodes for all elements
2648    *  \param [in] nbElems - nb of elements
2649    *  \param [in] nbNodes - nb of nodes in each element
2650    *  \param [in] mesh - the mesh
2651    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2652    *  \param [in] type - type of elements to look for
2653    */
2654   //================================================================================
2655
2656   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2657                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2658                     const int*                            interlace,
2659                     const int                             nbElems,
2660                     const int                             nbNodes,
2661                     SMESHDS_Mesh*                         mesh = 0,
2662                     list< const SMDS_MeshElement* >*      elemQueue=0,
2663                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2664   {
2665     for ( int iE = 0; iE < nbElems; ++iE )
2666     {
2667       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2668       const int*                         select = & interlace[iE*nbNodes];
2669       elemNodes.resize( nbNodes );
2670       for ( int iN = 0; iN < nbNodes; ++iN )
2671         elemNodes[iN] = srcNodes[ select[ iN ]];
2672     }
2673     const SMDS_MeshElement* e;
2674     if ( elemQueue )
2675       for ( int iE = 0; iE < nbElems; ++iE )
2676         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2677           elemQueue->push_back( e );
2678   }
2679 }
2680
2681 //=======================================================================
2682 /*
2683  * Split bi-quadratic elements into linear ones without creation of additional nodes
2684  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2685  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2686  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2687  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2688  *   will be split in order to keep the mesh conformal.
2689  *  \param elems - elements to split
2690  */
2691 //=======================================================================
2692
2693 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2694 {
2695   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2696   vector<const SMDS_MeshElement* > splitElems;
2697   list< const SMDS_MeshElement* > elemQueue;
2698   list< const SMDS_MeshElement* >::iterator elemIt;
2699
2700   SMESHDS_Mesh * mesh = GetMeshDS();
2701   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2702   int nbElems, nbNodes;
2703
2704   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2705   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2706   {
2707     elemQueue.clear();
2708     elemQueue.push_back( *elemSetIt );
2709     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2710     {
2711       const SMDS_MeshElement* elem = *elemIt;
2712       switch( elem->GetEntityType() )
2713       {
2714       case SMDSEntity_TriQuad_Hexa: // HEX27
2715       {
2716         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2717         nbElems  = nbNodes = 8;
2718         elemType = & hexaType;
2719
2720         // get nodes for new elements
2721         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2722                                  { 1,9,20,8,    17,22,26,21 },
2723                                  { 2,10,20,9,   18,23,26,22 },
2724                                  { 3,11,20,10,  19,24,26,23 },
2725                                  { 16,21,26,24, 4,12,25,15  },
2726                                  { 17,22,26,21, 5,13,25,12  },
2727                                  { 18,23,26,22, 6,14,25,13  },
2728                                  { 19,24,26,23, 7,15,25,14  }};
2729         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2730
2731         // add boundary faces to elemQueue
2732         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2733                                  { 4,5,6,7, 12,13,14,15, 25 },
2734                                  { 0,1,5,4, 8,17,12,16,  21 },
2735                                  { 1,2,6,5, 9,18,13,17,  22 },
2736                                  { 2,3,7,6, 10,19,14,18, 23 },
2737                                  { 3,0,4,7, 11,16,15,19, 24 }};
2738         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2739
2740         // add boundary segments to elemQueue
2741         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2742                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2743                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2744         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2745         break;
2746       }
2747       case SMDSEntity_BiQuad_Triangle: // TRIA7
2748       {
2749         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2750         nbElems = 3;
2751         nbNodes = 4;
2752         elemType = & quadType;
2753
2754         // get nodes for new elements
2755         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2756         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2757
2758         // add boundary segments to elemQueue
2759         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2760         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2761         break;
2762       }
2763       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2764       {
2765         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2766         nbElems = 4;
2767         nbNodes = 4;
2768         elemType = & quadType;
2769
2770         // get nodes for new elements
2771         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2772         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2773
2774         // add boundary segments to elemQueue
2775         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2776         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2777         break;
2778       }
2779       case SMDSEntity_Quad_Edge:
2780       {
2781         if ( elemIt == elemQueue.begin() )
2782           continue; // an elem is in theElems
2783         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2784         nbElems = 2;
2785         nbNodes = 2;
2786         elemType = & segType;
2787
2788         // get nodes for new elements
2789         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2790         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2791         break;
2792       }
2793       default: continue;
2794       } // switch( elem->GetEntityType() )
2795
2796       // Create new elements
2797
2798       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2799
2800       splitElems.clear();
2801
2802       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2803       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2804       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2805       //elemType->SetID( -1 );
2806
2807       for ( int iE = 0; iE < nbElems; ++iE )
2808         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2809
2810
2811       ReplaceElemInGroups( elem, splitElems, mesh );
2812
2813       if ( subMesh )
2814         for ( size_t i = 0; i < splitElems.size(); ++i )
2815           subMesh->AddElement( splitElems[i] );
2816     }
2817   }
2818 }
2819
2820 //=======================================================================
2821 //function : AddToSameGroups
2822 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2823 //=======================================================================
2824
2825 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2826                                         const SMDS_MeshElement* elemInGroups,
2827                                         SMESHDS_Mesh *          aMesh)
2828 {
2829   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2830   if (!groups.empty()) {
2831     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2832     for ( ; grIt != groups.end(); grIt++ ) {
2833       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2834       if ( group && group->Contains( elemInGroups ))
2835         group->SMDSGroup().Add( elemToAdd );
2836     }
2837   }
2838 }
2839
2840
2841 //=======================================================================
2842 //function : RemoveElemFromGroups
2843 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2844 //=======================================================================
2845 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2846                                              SMESHDS_Mesh *          aMesh)
2847 {
2848   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849   if (!groups.empty())
2850   {
2851     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2852     for (; GrIt != groups.end(); GrIt++)
2853     {
2854       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2855       if (!grp || grp->IsEmpty()) continue;
2856       grp->SMDSGroup().Remove(removeelem);
2857     }
2858   }
2859 }
2860
2861 //================================================================================
2862 /*!
2863  * \brief Replace elemToRm by elemToAdd in the all groups
2864  */
2865 //================================================================================
2866
2867 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2868                                             const SMDS_MeshElement* elemToAdd,
2869                                             SMESHDS_Mesh *          aMesh)
2870 {
2871   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872   if (!groups.empty()) {
2873     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2874     for ( ; grIt != groups.end(); grIt++ ) {
2875       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2876       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2877         group->SMDSGroup().Add( elemToAdd );
2878     }
2879   }
2880 }
2881
2882 //================================================================================
2883 /*!
2884  * \brief Replace elemToRm by elemToAdd in the all groups
2885  */
2886 //================================================================================
2887
2888 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2889                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2890                                             SMESHDS_Mesh *                         aMesh)
2891 {
2892   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2893   if (!groups.empty())
2894   {
2895     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896     for ( ; grIt != groups.end(); grIt++ ) {
2897       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2899         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2900           group->SMDSGroup().Add( elemToAdd[ i ] );
2901     }
2902   }
2903 }
2904
2905 //=======================================================================
2906 //function : QuadToTri
2907 //purpose  : Cut quadrangles into triangles.
2908 //           theCrit is used to select a diagonal to cut
2909 //=======================================================================
2910
2911 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2912                                   const bool         the13Diag)
2913 {
2914   ClearLastCreated();
2915   myLastCreatedElems.reserve( theElems.size() * 2 );
2916
2917   SMESHDS_Mesh *       aMesh = GetMeshDS();
2918   Handle(Geom_Surface) surface;
2919   SMESH_MesherHelper   helper( *GetMesh() );
2920
2921   TIDSortedElemSet::iterator itElem;
2922   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2923   {
2924     const SMDS_MeshElement* elem = *itElem;
2925     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2926       continue;
2927
2928     if ( elem->NbNodes() == 4 ) {
2929       // retrieve element nodes
2930       const SMDS_MeshNode* aNodes [4];
2931       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2932       int i = 0;
2933       while ( itN->more() )
2934         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2935
2936       int aShapeId = FindShape( elem );
2937       const SMDS_MeshElement* newElem1 = 0;
2938       const SMDS_MeshElement* newElem2 = 0;
2939       if ( the13Diag ) {
2940         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2941         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2942       }
2943       else {
2944         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2945         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2946       }
2947       myLastCreatedElems.push_back(newElem1);
2948       myLastCreatedElems.push_back(newElem2);
2949       // put a new triangle on the same shape and add to the same groups
2950       if ( aShapeId )
2951       {
2952         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2953         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2954       }
2955       AddToSameGroups( newElem1, elem, aMesh );
2956       AddToSameGroups( newElem2, elem, aMesh );
2957       aMesh->RemoveElement( elem );
2958     }
2959
2960     // Quadratic quadrangle
2961
2962     else if ( elem->NbNodes() >= 8 )
2963     {
2964       // get surface elem is on
2965       int aShapeId = FindShape( elem );
2966       if ( aShapeId != helper.GetSubShapeID() ) {
2967         surface.Nullify();
2968         TopoDS_Shape shape;
2969         if ( aShapeId > 0 )
2970           shape = aMesh->IndexToShape( aShapeId );
2971         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2972           TopoDS_Face face = TopoDS::Face( shape );
2973           surface = BRep_Tool::Surface( face );
2974           if ( !surface.IsNull() )
2975             helper.SetSubShape( shape );
2976         }
2977       }
2978
2979       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2980       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2981       for ( int i = 0; itN->more(); ++i )
2982         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2983
2984       const SMDS_MeshNode* centrNode = aNodes[8];
2985       if ( centrNode == 0 )
2986       {
2987         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2988                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2989                                            surface.IsNull() );
2990         myLastCreatedNodes.push_back(centrNode);
2991       }
2992
2993       // create a new element
2994       const SMDS_MeshElement* newElem1 = 0;
2995       const SMDS_MeshElement* newElem2 = 0;
2996       if ( the13Diag ) {
2997         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2998                                   aNodes[6], aNodes[7], centrNode );
2999         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3000                                   centrNode, aNodes[4], aNodes[5] );
3001       }
3002       else {
3003         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3004                                   aNodes[7], aNodes[4], centrNode );
3005         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3006                                   centrNode, aNodes[5], aNodes[6] );
3007       }
3008       myLastCreatedElems.push_back(newElem1);
3009       myLastCreatedElems.push_back(newElem2);
3010       // put a new triangle on the same shape and add to the same groups
3011       if ( aShapeId )
3012       {
3013         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3014         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3015       }
3016       AddToSameGroups( newElem1, elem, aMesh );
3017       AddToSameGroups( newElem2, elem, aMesh );
3018       aMesh->RemoveElement( elem );
3019     }
3020   }
3021
3022   return true;
3023 }
3024
3025 //=======================================================================
3026 //function : getAngle
3027 //purpose  :
3028 //=======================================================================
3029
3030 double getAngle(const SMDS_MeshElement * tr1,
3031                 const SMDS_MeshElement * tr2,
3032                 const SMDS_MeshNode *    n1,
3033                 const SMDS_MeshNode *    n2)
3034 {
3035   double angle = 2. * M_PI; // bad angle
3036
3037   // get normals
3038   SMESH::Controls::TSequenceOfXYZ P1, P2;
3039   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3040        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3041     return angle;
3042   gp_Vec N1,N2;
3043   if(!tr1->IsQuadratic())
3044     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3045   else
3046     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3047   if ( N1.SquareMagnitude() <= gp::Resolution() )
3048     return angle;
3049   if(!tr2->IsQuadratic())
3050     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3051   else
3052     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3053   if ( N2.SquareMagnitude() <= gp::Resolution() )
3054     return angle;
3055
3056   // find the first diagonal node n1 in the triangles:
3057   // take in account a diagonal link orientation
3058   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3059   for ( int t = 0; t < 2; t++ ) {
3060     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3061     int i = 0, iDiag = -1;
3062     while ( it->more()) {
3063       const SMDS_MeshElement *n = it->next();
3064       if ( n == n1 || n == n2 ) {
3065         if ( iDiag < 0)
3066           iDiag = i;
3067         else {
3068           if ( i - iDiag == 1 )
3069             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3070           else
3071             nFirst[ t ] = n;
3072           break;
3073         }
3074       }
3075       i++;
3076     }
3077   }
3078   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3079     N2.Reverse();
3080
3081   angle = N1.Angle( N2 );
3082   //SCRUTE( angle );
3083   return angle;
3084 }
3085
3086 // =================================================
3087 // class generating a unique ID for a pair of nodes
3088 // and able to return nodes by that ID
3089 // =================================================
3090 class LinkID_Gen {
3091 public:
3092
3093   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3094     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3095   {}
3096
3097   long GetLinkID (const SMDS_MeshNode * n1,
3098                   const SMDS_MeshNode * n2) const
3099   {
3100     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3101   }
3102
3103   bool GetNodes (const long             theLinkID,
3104                  const SMDS_MeshNode* & theNode1,
3105                  const SMDS_MeshNode* & theNode2) const
3106   {
3107     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3108     if ( !theNode1 ) return false;
3109     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3110     if ( !theNode2 ) return false;
3111     return true;
3112   }
3113
3114 private:
3115   LinkID_Gen();
3116   const SMESHDS_Mesh* myMesh;
3117   long                myMaxID;
3118 };
3119
3120
3121 //=======================================================================
3122 //function : TriToQuad
3123 //purpose  : Fuse neighbour triangles into quadrangles.
3124 //           theCrit is used to select a neighbour to fuse with.
3125 //           theMaxAngle is a max angle between element normals at which
3126 //           fusion is still performed.
3127 //=======================================================================
3128
3129 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3130                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3131                                   const double                         theMaxAngle)
3132 {
3133   ClearLastCreated();
3134   myLastCreatedElems.reserve( theElems.size() / 2 );
3135
3136   if ( !theCrit.get() )
3137     return false;
3138
3139   SMESHDS_Mesh * aMesh = GetMeshDS();
3140
3141   // Prepare data for algo: build
3142   // 1. map of elements with their linkIDs
3143   // 2. map of linkIDs with their elements
3144
3145   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3146   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3147   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3148   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3149
3150   TIDSortedElemSet::iterator itElem;
3151   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3152   {
3153     const SMDS_MeshElement* elem = *itElem;
3154     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3155     bool IsTria = ( elem->NbCornerNodes()==3 );
3156     if (!IsTria) continue;
3157
3158     // retrieve element nodes
3159     const SMDS_MeshNode* aNodes [4];
3160     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3161     int i = 0;
3162     while ( i < 3 )
3163       aNodes[ i++ ] = itN->next();
3164     aNodes[ 3 ] = aNodes[ 0 ];
3165
3166     // fill maps
3167     for ( i = 0; i < 3; i++ ) {
3168       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3169       // check if elements sharing a link can be fused
3170       itLE = mapLi_listEl.find( link );
3171       if ( itLE != mapLi_listEl.end() ) {
3172         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3173           continue;
3174         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3175         //if ( FindShape( elem ) != FindShape( elem2 ))
3176         //  continue; // do not fuse triangles laying on different shapes
3177         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3178           continue; // avoid making badly shaped quads
3179         (*itLE).second.push_back( elem );
3180       }
3181       else {
3182         mapLi_listEl[ link ].push_back( elem );
3183       }
3184       mapEl_setLi [ elem ].insert( link );
3185     }
3186   }
3187   // Clean the maps from the links shared by a sole element, ie
3188   // links to which only one element is bound in mapLi_listEl
3189
3190   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3191     int nbElems = (*itLE).second.size();
3192     if ( nbElems < 2  ) {
3193       const SMDS_MeshElement* elem = (*itLE).second.front();
3194       SMESH_TLink link = (*itLE).first;
3195       mapEl_setLi[ elem ].erase( link );
3196       if ( mapEl_setLi[ elem ].empty() )
3197         mapEl_setLi.erase( elem );
3198     }
3199   }
3200
3201   // Algo: fuse triangles into quadrangles
3202
3203   while ( ! mapEl_setLi.empty() ) {
3204     // Look for the start element:
3205     // the element having the least nb of shared links
3206     const SMDS_MeshElement* startElem = 0;
3207     int minNbLinks = 4;
3208     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3209       int nbLinks = (*itEL).second.size();
3210       if ( nbLinks < minNbLinks ) {
3211         startElem = (*itEL).first;
3212         minNbLinks = nbLinks;
3213         if ( minNbLinks == 1 )
3214           break;
3215       }
3216     }
3217
3218     // search elements to fuse starting from startElem or links of elements
3219     // fused earlyer - startLinks
3220     list< SMESH_TLink > startLinks;
3221     while ( startElem || !startLinks.empty() ) {
3222       while ( !startElem && !startLinks.empty() ) {
3223         // Get an element to start, by a link
3224         SMESH_TLink linkId = startLinks.front();
3225         startLinks.pop_front();
3226         itLE = mapLi_listEl.find( linkId );
3227         if ( itLE != mapLi_listEl.end() ) {
3228           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3229           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3230           for ( ; itE != listElem.end() ; itE++ )
3231             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3232               startElem = (*itE);
3233           mapLi_listEl.erase( itLE );
3234         }
3235       }
3236
3237       if ( startElem ) {
3238         // Get candidates to be fused
3239         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3240         const SMESH_TLink *link12 = 0, *link13 = 0;
3241         startElem = 0;
3242         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3243         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3244         ASSERT( !setLi.empty() );
3245         set< SMESH_TLink >::iterator itLi;
3246         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3247         {
3248           const SMESH_TLink & link = (*itLi);
3249           itLE = mapLi_listEl.find( link );
3250           if ( itLE == mapLi_listEl.end() )
3251             continue;
3252
3253           const SMDS_MeshElement* elem = (*itLE).second.front();
3254           if ( elem == tr1 )
3255             elem = (*itLE).second.back();
3256           mapLi_listEl.erase( itLE );
3257           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3258             continue;
3259           if ( tr2 ) {
3260             tr3 = elem;
3261             link13 = &link;
3262           }
3263           else {
3264             tr2 = elem;
3265             link12 = &link;
3266           }
3267
3268           // add other links of elem to list of links to re-start from
3269           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3270           set< SMESH_TLink >::iterator it;
3271           for ( it = links.begin(); it != links.end(); it++ ) {
3272             const SMESH_TLink& link2 = (*it);
3273             if ( link2 != link )
3274               startLinks.push_back( link2 );
3275           }
3276         }
3277
3278         // Get nodes of possible quadrangles
3279         const SMDS_MeshNode *n12 [4], *n13 [4];
3280         bool Ok12 = false, Ok13 = false;
3281         const SMDS_MeshNode *linkNode1, *linkNode2;
3282         if(tr2) {
3283           linkNode1 = link12->first;
3284           linkNode2 = link12->second;
3285           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3286             Ok12 = true;
3287         }
3288         if(tr3) {
3289           linkNode1 = link13->first;
3290           linkNode2 = link13->second;
3291           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3292             Ok13 = true;
3293         }
3294
3295         // Choose a pair to fuse
3296         if ( Ok12 && Ok13 ) {
3297           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3298           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3299           double aBadRate12 = getBadRate( &quad12, theCrit );
3300           double aBadRate13 = getBadRate( &quad13, theCrit );
3301           if (  aBadRate13 < aBadRate12 )
3302             Ok12 = false;
3303           else
3304             Ok13 = false;
3305         }
3306
3307         // Make quadrangles
3308         // and remove fused elems and remove links from the maps
3309         mapEl_setLi.erase( tr1 );
3310         if ( Ok12 )
3311         {
3312           mapEl_setLi.erase( tr2 );
3313           mapLi_listEl.erase( *link12 );
3314           if ( tr1->NbNodes() == 3 )
3315           {
3316             const SMDS_MeshElement* newElem = 0;
3317             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3318             myLastCreatedElems.push_back(newElem);
3319             AddToSameGroups( newElem, tr1, aMesh );
3320             int aShapeId = tr1->getshapeId();
3321             if ( aShapeId )
3322               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3323             aMesh->RemoveElement( tr1 );
3324             aMesh->RemoveElement( tr2 );
3325           }
3326           else {
3327             vector< const SMDS_MeshNode* > N1;
3328             vector< const SMDS_MeshNode* > N2;
3329             getNodesFromTwoTria(tr1,tr2,N1,N2);
3330             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3331             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3332             // i.e. first nodes from both arrays form a new diagonal
3333             const SMDS_MeshNode* aNodes[8];
3334             aNodes[0] = N1[0];
3335             aNodes[1] = N1[1];
3336             aNodes[2] = N2[0];
3337             aNodes[3] = N2[1];
3338             aNodes[4] = N1[3];
3339             aNodes[5] = N2[5];
3340             aNodes[6] = N2[3];
3341             aNodes[7] = N1[5];
3342             const SMDS_MeshElement* newElem = 0;
3343             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3344               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3345                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3346             else
3347               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3348                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3349             myLastCreatedElems.push_back(newElem);
3350             AddToSameGroups( newElem, tr1, aMesh );
3351             int aShapeId = tr1->getshapeId();
3352             if ( aShapeId )
3353               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3354             aMesh->RemoveElement( tr1 );
3355             aMesh->RemoveElement( tr2 );
3356             // remove middle node (9)
3357             if ( N1[4]->NbInverseElements() == 0 )
3358               aMesh->RemoveNode( N1[4] );
3359             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3360               aMesh->RemoveNode( N1[6] );
3361             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N2[6] );
3363           }
3364         }
3365         else if ( Ok13 )
3366         {
3367           mapEl_setLi.erase( tr3 );
3368           mapLi_listEl.erase( *link13 );
3369           if ( tr1->NbNodes() == 3 ) {
3370             const SMDS_MeshElement* newElem = 0;
3371             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3372             myLastCreatedElems.push_back(newElem);
3373             AddToSameGroups( newElem, tr1, aMesh );
3374             int aShapeId = tr1->getshapeId();
3375             if ( aShapeId )
3376               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377             aMesh->RemoveElement( tr1 );
3378             aMesh->RemoveElement( tr3 );
3379           }
3380           else {
3381             vector< const SMDS_MeshNode* > N1;
3382             vector< const SMDS_MeshNode* > N2;
3383             getNodesFromTwoTria(tr1,tr3,N1,N2);
3384             // now we receive following N1 and N2 (using numeration as above image)
3385             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3386             // i.e. first nodes from both arrays form a new diagonal
3387             const SMDS_MeshNode* aNodes[8];
3388             aNodes[0] = N1[0];
3389             aNodes[1] = N1[1];
3390             aNodes[2] = N2[0];
3391             aNodes[3] = N2[1];
3392             aNodes[4] = N1[3];
3393             aNodes[5] = N2[5];
3394             aNodes[6] = N2[3];
3395             aNodes[7] = N1[5];
3396             const SMDS_MeshElement* newElem = 0;
3397             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3398               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3399                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3400             else
3401               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3402                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3403             myLastCreatedElems.push_back(newElem);
3404             AddToSameGroups( newElem, tr1, aMesh );
3405             int aShapeId = tr1->getshapeId();
3406             if ( aShapeId )
3407               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3408             aMesh->RemoveElement( tr1 );
3409             aMesh->RemoveElement( tr3 );
3410             // remove middle node (9)
3411             if ( N1[4]->NbInverseElements() == 0 )
3412               aMesh->RemoveNode( N1[4] );
3413             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3414               aMesh->RemoveNode( N1[6] );
3415             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N2[6] );
3417           }
3418         }
3419
3420         // Next element to fuse: the rejected one
3421         if ( tr3 )
3422           startElem = Ok12 ? tr3 : tr2;
3423
3424       } // if ( startElem )
3425     } // while ( startElem || !startLinks.empty() )
3426   } // while ( ! mapEl_setLi.empty() )
3427
3428   return true;
3429 }
3430
3431 //================================================================================
3432 /*!
3433  * \brief Return nodes linked to the given one
3434  * \param theNode - the node
3435  * \param linkedNodes - the found nodes
3436  * \param type - the type of elements to check
3437  *
3438  * Medium nodes are ignored
3439  */
3440 //================================================================================
3441
3442 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3443                                        TIDSortedElemSet &   linkedNodes,
3444                                        SMDSAbs_ElementType  type )
3445 {
3446   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3447   while ( elemIt->more() )
3448   {
3449     const SMDS_MeshElement* elem = elemIt->next();
3450     if(elem->GetType() == SMDSAbs_0DElement)
3451       continue;
3452
3453     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3454     if ( elem->GetType() == SMDSAbs_Volume )
3455     {
3456       SMDS_VolumeTool vol( elem );
3457       while ( nodeIt->more() ) {
3458         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3459         if ( theNode != n && vol.IsLinked( theNode, n ))
3460           linkedNodes.insert( n );
3461       }
3462     }
3463     else
3464     {
3465       for ( int i = 0; nodeIt->more(); ++i ) {
3466         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3467         if ( n == theNode ) {
3468           int iBefore = i - 1;
3469           int iAfter  = i + 1;
3470           if ( elem->IsQuadratic() ) {
3471             int nb = elem->NbNodes() / 2;
3472             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3473             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3474           }
3475           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3476           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3477         }
3478       }
3479     }
3480   }
3481 }
3482
3483 //=======================================================================
3484 //function : laplacianSmooth
3485 //purpose  : pulls theNode toward the center of surrounding nodes directly
3486 //           connected to that node along an element edge
3487 //=======================================================================
3488
3489 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3490                      const Handle(Geom_Surface)&          theSurface,
3491                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3492 {
3493   // find surrounding nodes
3494
3495   TIDSortedElemSet nodeSet;
3496   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3497
3498   // compute new coodrs
3499
3500   double coord[] = { 0., 0., 0. };
3501   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3502   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3503     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3504     if ( theSurface.IsNull() ) { // smooth in 3D
3505       coord[0] += node->X();
3506       coord[1] += node->Y();
3507       coord[2] += node->Z();
3508     }
3509     else { // smooth in 2D
3510       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3511       gp_XY* uv = theUVMap[ node ];
3512       coord[0] += uv->X();
3513       coord[1] += uv->Y();
3514     }
3515   }
3516   int nbNodes = nodeSet.size();
3517   if ( !nbNodes )
3518     return;
3519   coord[0] /= nbNodes;
3520   coord[1] /= nbNodes;
3521
3522   if ( !theSurface.IsNull() ) {
3523     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3524     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3525     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3526     coord[0] = p3d.X();
3527     coord[1] = p3d.Y();
3528     coord[2] = p3d.Z();
3529   }
3530   else
3531     coord[2] /= nbNodes;
3532
3533   // move node
3534
3535   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3536 }
3537
3538 //=======================================================================
3539 //function : centroidalSmooth
3540 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3541 //           surrounding elements
3542 //=======================================================================
3543
3544 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3545                       const Handle(Geom_Surface)&          theSurface,
3546                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3547 {
3548   gp_XYZ aNewXYZ(0.,0.,0.);
3549   SMESH::Controls::Area anAreaFunc;
3550   double totalArea = 0.;
3551   int nbElems = 0;
3552
3553   // compute new XYZ
3554
3555   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3556   while ( elemIt->more() )
3557   {
3558     const SMDS_MeshElement* elem = elemIt->next();
3559     nbElems++;
3560
3561     gp_XYZ elemCenter(0.,0.,0.);
3562     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3563     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3564     int nn = elem->NbNodes();
3565     if(elem->IsQuadratic()) nn = nn/2;
3566     int i=0;
3567     //while ( itN->more() ) {
3568     while ( i<nn ) {
3569       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3570       i++;
3571       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3572       aNodePoints.push_back( aP );
3573       if ( !theSurface.IsNull() ) { // smooth in 2D
3574         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3575         gp_XY* uv = theUVMap[ aNode ];
3576         aP.SetCoord( uv->X(), uv->Y(), 0. );
3577       }
3578       elemCenter += aP;
3579     }
3580     double elemArea = anAreaFunc.GetValue( aNodePoints );
3581     totalArea += elemArea;
3582     elemCenter /= nn;
3583     aNewXYZ += elemCenter * elemArea;
3584   }
3585   aNewXYZ /= totalArea;
3586   if ( !theSurface.IsNull() ) {
3587     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3588     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3589   }
3590
3591   // move node
3592
3593   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3594 }
3595
3596 //=======================================================================
3597 //function : getClosestUV
3598 //purpose  : return UV of closest projection
3599 //=======================================================================
3600
3601 static bool getClosestUV (Extrema_GenExtPS& projector,
3602                           const gp_Pnt&     point,
3603                           gp_XY &           result)
3604 {
3605   projector.Perform( point );
3606   if ( projector.IsDone() ) {
3607     double u, v, minVal = DBL_MAX;
3608     for ( int i = projector.NbExt(); i > 0; i-- )
3609       if ( projector.SquareDistance( i ) < minVal ) {
3610         minVal = projector.SquareDistance( i );
3611         projector.Point( i ).Parameter( u, v );
3612       }
3613     result.SetCoord( u, v );
3614     return true;
3615   }
3616   return false;
3617 }
3618
3619 //=======================================================================
3620 //function : Smooth
3621 //purpose  : Smooth theElements during theNbIterations or until a worst
3622 //           element has aspect ratio <= theTgtAspectRatio.
3623 //           Aspect Ratio varies in range [1.0, inf].
3624 //           If theElements is empty, the whole mesh is smoothed.
3625 //           theFixedNodes contains additionally fixed nodes. Nodes built
3626 //           on edges and boundary nodes are always fixed.
3627 //=======================================================================
3628
3629 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3630                                set<const SMDS_MeshNode*> & theFixedNodes,
3631                                const SmoothMethod          theSmoothMethod,
3632                                const int                   theNbIterations,
3633                                double                      theTgtAspectRatio,
3634                                const bool                  the2D)
3635 {
3636   ClearLastCreated();
3637
3638   if ( theTgtAspectRatio < 1.0 )
3639     theTgtAspectRatio = 1.0;
3640
3641   const double disttol = 1.e-16;
3642
3643   SMESH::Controls::AspectRatio aQualityFunc;
3644
3645   SMESHDS_Mesh* aMesh = GetMeshDS();
3646
3647   if ( theElems.empty() ) {
3648     // add all faces to theElems
3649     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3650     while ( fIt->more() ) {
3651       const SMDS_MeshElement* face = fIt->next();
3652       theElems.insert( theElems.end(), face );
3653     }
3654   }
3655   // get all face ids theElems are on
3656   set< int > faceIdSet;
3657   TIDSortedElemSet::iterator itElem;
3658   if ( the2D )
3659     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3660       int fId = FindShape( *itElem );
3661       // check that corresponding submesh exists and a shape is face
3662       if (fId &&
3663           faceIdSet.find( fId ) == faceIdSet.end() &&
3664           aMesh->MeshElements( fId )) {
3665         TopoDS_Shape F = aMesh->IndexToShape( fId );
3666         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3667           faceIdSet.insert( fId );
3668       }
3669     }
3670   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3671
3672   // ===============================================
3673   // smooth elements on each TopoDS_Face separately
3674   // ===============================================
3675
3676   SMESH_MesherHelper helper( *GetMesh() );
3677
3678   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3679   for ( ; fId != faceIdSet.rend(); ++fId )
3680   {
3681     // get face surface and submesh
3682     Handle(Geom_Surface) surface;
3683     SMESHDS_SubMesh* faceSubMesh = 0;
3684     TopoDS_Face face;
3685     double fToler2 = 0;
3686     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3687     bool isUPeriodic = false, isVPeriodic = false;
3688     if ( *fId )
3689     {
3690       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3691       surface = BRep_Tool::Surface( face );
3692       faceSubMesh = aMesh->MeshElements( *fId );
3693       fToler2 = BRep_Tool::Tolerance( face );
3694       fToler2 *= fToler2 * 10.;
3695       isUPeriodic = surface->IsUPeriodic();
3696       // if ( isUPeriodic )
3697       //   surface->UPeriod();
3698       isVPeriodic = surface->IsVPeriodic();
3699       // if ( isVPeriodic )
3700       //   surface->VPeriod();
3701       surface->Bounds( u1, u2, v1, v2 );
3702       helper.SetSubShape( face );
3703     }
3704     // ---------------------------------------------------------
3705     // for elements on a face, find movable and fixed nodes and
3706     // compute UV for them
3707     // ---------------------------------------------------------
3708     bool checkBoundaryNodes = false;
3709     bool isQuadratic = false;
3710     set<const SMDS_MeshNode*> setMovableNodes;
3711     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3712     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3713     list< const SMDS_MeshElement* > elemsOnFace;
3714
3715     Extrema_GenExtPS projector;
3716     GeomAdaptor_Surface surfAdaptor;
3717     if ( !surface.IsNull() ) {
3718       surfAdaptor.Load( surface );
3719       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3720     }
3721     int nbElemOnFace = 0;
3722     itElem = theElems.begin();
3723     // loop on not yet smoothed elements: look for elems on a face
3724     while ( itElem != theElems.end() )
3725     {
3726       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3727         break; // all elements found
3728
3729       const SMDS_MeshElement* elem = *itElem;
3730       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3731            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3732         ++itElem;
3733         continue;
3734       }
3735       elemsOnFace.push_back( elem );
3736       theElems.erase( itElem++ );
3737       nbElemOnFace++;
3738
3739       if ( !isQuadratic )
3740         isQuadratic = elem->IsQuadratic();
3741
3742       // get movable nodes of elem
3743       const SMDS_MeshNode* node;
3744       SMDS_TypeOfPosition posType;
3745       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3746       int nn = 0, nbn =  elem->NbNodes();
3747       if(elem->IsQuadratic())
3748         nbn = nbn/2;
3749       while ( nn++ < nbn ) {
3750         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3751         const SMDS_PositionPtr& pos = node->GetPosition();
3752         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3753         if (posType != SMDS_TOP_EDGE &&
3754             posType != SMDS_TOP_VERTEX &&
3755             theFixedNodes.find( node ) == theFixedNodes.end())
3756         {
3757           // check if all faces around the node are on faceSubMesh
3758           // because a node on edge may be bound to face
3759           bool all = true;
3760           if ( faceSubMesh ) {
3761             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3762             while ( eIt->more() && all ) {
3763               const SMDS_MeshElement* e = eIt->next();
3764               all = faceSubMesh->Contains( e );
3765             }
3766           }
3767           if ( all )
3768             setMovableNodes.insert( node );
3769           else
3770             checkBoundaryNodes = true;
3771         }
3772         if ( posType == SMDS_TOP_3DSPACE )
3773           checkBoundaryNodes = true;
3774       }
3775
3776       if ( surface.IsNull() )
3777         continue;
3778
3779       // get nodes to check UV
3780       list< const SMDS_MeshNode* > uvCheckNodes;
3781       const SMDS_MeshNode* nodeInFace = 0;
3782       itN = elem->nodesIterator();
3783       nn = 0; nbn =  elem->NbNodes();
3784       if(elem->IsQuadratic())
3785         nbn = nbn/2;
3786       while ( nn++ < nbn ) {
3787         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3788         if ( node->GetPosition()->GetDim() == 2 )
3789           nodeInFace = node;
3790         if ( uvMap.find( node ) == uvMap.end() )
3791           uvCheckNodes.push_back( node );
3792         // add nodes of elems sharing node
3793         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3794         //         while ( eIt->more() ) {
3795         //           const SMDS_MeshElement* e = eIt->next();
3796         //           if ( e != elem ) {
3797         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3798         //             while ( nIt->more() ) {
3799         //               const SMDS_MeshNode* n =
3800         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3801         //               if ( uvMap.find( n ) == uvMap.end() )
3802         //                 uvCheckNodes.push_back( n );
3803         //             }
3804         //           }
3805         //         }
3806       }
3807       // check UV on face
3808       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3809       for ( ; n != uvCheckNodes.end(); ++n ) {
3810         node = *n;
3811         gp_XY uv( 0, 0 );
3812         const SMDS_PositionPtr& pos = node->GetPosition();
3813         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3814         // get existing UV
3815         if ( pos )
3816         {
3817           bool toCheck = true;
3818           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3819         }
3820         // compute not existing UV
3821         bool project = ( posType == SMDS_TOP_3DSPACE );
3822         // double dist1 = DBL_MAX, dist2 = 0;
3823         // if ( posType != SMDS_TOP_3DSPACE ) {
3824         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3825         //   project = dist1 > fToler2;
3826         // }
3827         if ( project ) { // compute new UV
3828           gp_XY newUV;
3829           gp_Pnt pNode = SMESH_NodeXYZ( node );
3830           if ( !getClosestUV( projector, pNode, newUV )) {
3831             MESSAGE("Node Projection Failed " << node);
3832           }
3833           else {
3834             if ( isUPeriodic )
3835               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3836             if ( isVPeriodic )
3837               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3838             // check new UV
3839             // if ( posType != SMDS_TOP_3DSPACE )
3840             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3841             // if ( dist2 < dist1 )
3842             uv = newUV;
3843           }
3844         }
3845         // store UV in the map
3846         listUV.push_back( uv );
3847         uvMap.insert( make_pair( node, &listUV.back() ));
3848       }
3849     } // loop on not yet smoothed elements
3850
3851     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3852       checkBoundaryNodes = true;
3853
3854     // fix nodes on mesh boundary
3855
3856     if ( checkBoundaryNodes ) {
3857       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3858       map< SMESH_TLink, int >::iterator link_nb;
3859       // put all elements links to linkNbMap
3860       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3861       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3862         const SMDS_MeshElement* elem = (*elemIt);
3863         int nbn =  elem->NbCornerNodes();
3864         // loop on elem links: insert them in linkNbMap
3865         for ( int iN = 0; iN < nbn; ++iN ) {
3866           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3867           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3868           SMESH_TLink link( n1, n2 );
3869           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3870           link_nb->second++;
3871         }
3872       }
3873       // remove nodes that are in links encountered only once from setMovableNodes
3874       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3875         if ( link_nb->second == 1 ) {
3876           setMovableNodes.erase( link_nb->first.node1() );
3877           setMovableNodes.erase( link_nb->first.node2() );
3878         }
3879       }
3880     }
3881
3882     // -----------------------------------------------------
3883     // for nodes on seam edge, compute one more UV ( uvMap2 );
3884     // find movable nodes linked to nodes on seam and which
3885     // are to be smoothed using the second UV ( uvMap2 )
3886     // -----------------------------------------------------
3887
3888     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3889     if ( !surface.IsNull() ) {
3890       TopExp_Explorer eExp( face, TopAbs_EDGE );
3891       for ( ; eExp.More(); eExp.Next() ) {
3892         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3893         if ( !BRep_Tool::IsClosed( edge, face ))
3894           continue;
3895         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3896         if ( !sm ) continue;
3897         // find out which parameter varies for a node on seam
3898         double f,l;
3899         gp_Pnt2d uv1, uv2;
3900         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3901         if ( pcurve.IsNull() ) continue;
3902         uv1 = pcurve->Value( f );
3903         edge.Reverse();
3904         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3905         if ( pcurve.IsNull() ) continue;
3906         uv2 = pcurve->Value( f );
3907         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3908         // assure uv1 < uv2
3909         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3910           std::swap( uv1, uv2 );
3911         // get nodes on seam and its vertices
3912         list< const SMDS_MeshNode* > seamNodes;
3913         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3914         while ( nSeamIt->more() ) {
3915           const SMDS_MeshNode* node = nSeamIt->next();
3916           if ( !isQuadratic || !IsMedium( node ))
3917             seamNodes.push_back( node );
3918         }
3919         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3920         for ( ; vExp.More(); vExp.Next() ) {
3921           sm = aMesh->MeshElements( vExp.Current() );
3922           if ( sm ) {
3923             nSeamIt = sm->GetNodes();
3924             while ( nSeamIt->more() )
3925               seamNodes.push_back( nSeamIt->next() );
3926           }
3927         }
3928         // loop on nodes on seam
3929         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3930         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3931           const SMDS_MeshNode* nSeam = *noSeIt;
3932           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3933           if ( n_uv == uvMap.end() )
3934             continue;
3935           // set the first UV
3936           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3937           // set the second UV
3938           listUV.push_back( *n_uv->second );
3939           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3940           if ( uvMap2.empty() )
3941             uvMap2 = uvMap; // copy the uvMap contents
3942           uvMap2[ nSeam ] = &listUV.back();
3943
3944           // collect movable nodes linked to ones on seam in nodesNearSeam
3945           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3946           while ( eIt->more() ) {
3947             const SMDS_MeshElement* e = eIt->next();
3948             int nbUseMap1 = 0, nbUseMap2 = 0;
3949             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3950             int nn = 0, nbn =  e->NbNodes();
3951             if(e->IsQuadratic()) nbn = nbn/2;
3952             while ( nn++ < nbn )
3953             {
3954               const SMDS_MeshNode* n =
3955                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3956               if (n == nSeam ||
3957                   setMovableNodes.find( n ) == setMovableNodes.end() )
3958                 continue;
3959               // add only nodes being closer to uv2 than to uv1
3960               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3961               //              0.5 * ( n->Y() + nSeam->Y() ),
3962               //              0.5 * ( n->Z() + nSeam->Z() ));
3963               // gp_XY uv;
3964               // getClosestUV( projector, pMid, uv );
3965               double x = uvMap[ n ]->Coord( iPar );
3966               if ( Abs( uv1.Coord( iPar ) - x ) >
3967                    Abs( uv2.Coord( iPar ) - x )) {
3968                 nodesNearSeam.insert( n );
3969                 nbUseMap2++;
3970               }
3971               else
3972                 nbUseMap1++;
3973             }
3974             // for centroidalSmooth all element nodes must
3975             // be on one side of a seam
3976             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3977               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3978               nn = 0;
3979               while ( nn++ < nbn ) {
3980                 const SMDS_MeshNode* n =
3981                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3982                 setMovableNodes.erase( n );
3983               }
3984             }
3985           }
3986         } // loop on nodes on seam
3987       } // loop on edge of a face
3988     } // if ( !face.IsNull() )
3989
3990     if ( setMovableNodes.empty() ) {
3991       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3992       continue; // goto next face
3993     }
3994
3995     // -------------
3996     // SMOOTHING //
3997     // -------------
3998
3999     int it = -1;
4000     double maxRatio = -1., maxDisplacement = -1.;
4001     set<const SMDS_MeshNode*>::iterator nodeToMove;
4002     for ( it = 0; it < theNbIterations; it++ ) {
4003       maxDisplacement = 0.;
4004       nodeToMove = setMovableNodes.begin();
4005       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4006         const SMDS_MeshNode* node = (*nodeToMove);
4007         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4008
4009         // smooth
4010         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4011         if ( theSmoothMethod == LAPLACIAN )
4012           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4013         else
4014           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015
4016         // node displacement
4017         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4018         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4019         if ( aDispl > maxDisplacement )
4020           maxDisplacement = aDispl;
4021       }
4022       // no node movement => exit
4023       //if ( maxDisplacement < 1.e-16 ) {
4024       if ( maxDisplacement < disttol ) {
4025         MESSAGE("-- no node movement --");
4026         break;
4027       }
4028
4029       // check elements quality
4030       maxRatio  = 0;
4031       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4032       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4033         const SMDS_MeshElement* elem = (*elemIt);
4034         if ( !elem || elem->GetType() != SMDSAbs_Face )
4035           continue;
4036         SMESH::Controls::TSequenceOfXYZ aPoints;
4037         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4038           double aValue = aQualityFunc.GetValue( aPoints );
4039           if ( aValue > maxRatio )
4040             maxRatio = aValue;
4041         }
4042       }
4043       if ( maxRatio <= theTgtAspectRatio ) {
4044         //MESSAGE("-- quality achieved --");
4045         break;
4046       }
4047       if (it+1 == theNbIterations) {
4048         //MESSAGE("-- Iteration limit exceeded --");
4049       }
4050     } // smoothing iterations
4051
4052     // MESSAGE(" Face id: " << *fId <<
4053     //         " Nb iterstions: " << it <<
4054     //         " Displacement: " << maxDisplacement <<
4055     //         " Aspect Ratio " << maxRatio);
4056
4057     // ---------------------------------------
4058     // new nodes positions are computed,
4059     // record movement in DS and set new UV
4060     // ---------------------------------------
4061     nodeToMove = setMovableNodes.begin();
4062     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4063       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4064       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4065       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4066       if ( node_uv != uvMap.end() ) {
4067         gp_XY* uv = node_uv->second;
4068         node->SetPosition
4069           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4070       }
4071     }
4072
4073     // move medium nodes of quadratic elements
4074     if ( isQuadratic )
4075     {
4076       vector<const SMDS_MeshNode*> nodes;
4077       bool checkUV;
4078       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4079       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4080       {
4081         const SMDS_MeshElement* QF = *elemIt;
4082         if ( QF->IsQuadratic() )
4083         {
4084           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4085                         SMDS_MeshElement::iterator() );
4086           nodes.push_back( nodes[0] );
4087           gp_Pnt xyz;
4088           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4089           {
4090             if ( !surface.IsNull() )
4091             {
4092               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4093               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4094               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4095               xyz = surface->Value( uv.X(), uv.Y() );
4096             }
4097             else {
4098               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4099             }
4100             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4101               // we have to move a medium node
4102               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4103           }
4104         }
4105       }
4106     }
4107
4108   } // loop on face ids
4109
4110 }
4111
4112 namespace
4113 {
4114   //=======================================================================
4115   //function : isReverse
4116   //purpose  : Return true if normal of prevNodes is not co-directied with
4117   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4118   //           iNotSame is where prevNodes and nextNodes are different.
4119   //           If result is true then future volume orientation is OK
4120   //=======================================================================
4121
4122   bool isReverse(const SMDS_MeshElement*             face,
4123                  const vector<const SMDS_MeshNode*>& prevNodes,
4124                  const vector<const SMDS_MeshNode*>& nextNodes,
4125                  const int                           iNotSame)
4126   {
4127
4128     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4129     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4130     gp_XYZ extrDir( pN - pP ), faceNorm;
4131     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4132
4133     return faceNorm * extrDir < 0.0;
4134   }
4135
4136   //================================================================================
4137   /*!
4138    * \brief Assure that theElemSets[0] holds elements, not nodes
4139    */
4140   //================================================================================
4141
4142   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4143   {
4144     if ( !theElemSets[0].empty() &&
4145          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4146     {
4147       std::swap( theElemSets[0], theElemSets[1] );
4148     }
4149     else if ( !theElemSets[1].empty() &&
4150               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4151     {
4152       std::swap( theElemSets[0], theElemSets[1] );
4153     }
4154   }
4155 }
4156
4157 //=======================================================================
4158 /*!
4159  * \brief Create elements by sweeping an element
4160  * \param elem - element to sweep
4161  * \param newNodesItVec - nodes generated from each node of the element
4162  * \param newElems - generated elements
4163  * \param nbSteps - number of sweeping steps
4164  * \param srcElements - to append elem for each generated element
4165  */
4166 //=======================================================================
4167
4168 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4169                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4170                                     list<const SMDS_MeshElement*>&        newElems,
4171                                     const size_t                          nbSteps,
4172                                     SMESH_SequenceOfElemPtr&              srcElements)
4173 {
4174   SMESHDS_Mesh* aMesh = GetMeshDS();
4175
4176   const int           nbNodes = elem->NbNodes();
4177   const int         nbCorners = elem->NbCornerNodes();
4178   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4179                                                           polyhedron creation !!! */
4180   // Loop on elem nodes:
4181   // find new nodes and detect same nodes indices
4182   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4183   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4184   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4185   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4186
4187   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4188   vector<int> sames(nbNodes);
4189   vector<bool> isSingleNode(nbNodes);
4190
4191   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4192     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4193     const SMDS_MeshNode*                         node = nnIt->first;
4194     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4195     if ( listNewNodes.empty() )
4196       return;
4197
4198     itNN   [ iNode ] = listNewNodes.begin();
4199     prevNod[ iNode ] = node;
4200     nextNod[ iNode ] = listNewNodes.front();
4201
4202     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4203                                                              corner node of linear */
4204     if ( prevNod[ iNode ] != nextNod [ iNode ])
4205       nbDouble += !isSingleNode[iNode];
4206
4207     if( iNode < nbCorners ) { // check corners only
4208       if ( prevNod[ iNode ] == nextNod [ iNode ])
4209         sames[nbSame++] = iNode;
4210       else
4211         iNotSameNode = iNode;
4212     }
4213   }
4214
4215   if ( nbSame == nbNodes || nbSame > 2) {
4216     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4217     return;
4218   }
4219
4220   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4221   {
4222     // fix nodes order to have bottom normal external
4223     if ( baseType == SMDSEntity_Polygon )
4224     {
4225       std::reverse( itNN.begin(), itNN.end() );
4226       std::reverse( prevNod.begin(), prevNod.end() );
4227       std::reverse( midlNod.begin(), midlNod.end() );
4228       std::reverse( nextNod.begin(), nextNod.end() );
4229       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4230     }
4231     else
4232     {
4233       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4234       SMDS_MeshCell::applyInterlace( ind, itNN );
4235       SMDS_MeshCell::applyInterlace( ind, prevNod );
4236       SMDS_MeshCell::applyInterlace( ind, nextNod );
4237       SMDS_MeshCell::applyInterlace( ind, midlNod );
4238       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4239       if ( nbSame > 0 )
4240       {
4241         sames[nbSame] = iNotSameNode;
4242         for ( int j = 0; j <= nbSame; ++j )
4243           for ( size_t i = 0; i < ind.size(); ++i )
4244             if ( ind[i] == sames[j] )
4245             {
4246               sames[j] = i;
4247               break;
4248             }
4249         iNotSameNode = sames[nbSame];
4250       }
4251     }
4252   }
4253   else if ( elem->GetType() == SMDSAbs_Edge )
4254   {
4255     // orient a new face same as adjacent one
4256     int i1, i2;
4257     const SMDS_MeshElement* e;
4258     TIDSortedElemSet dummy;
4259     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4260         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4261         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4262     {
4263       // there is an adjacent face, check order of nodes in it
4264       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4265       if ( sameOrder )
4266       {
4267         std::swap( itNN[0],    itNN[1] );
4268         std::swap( prevNod[0], prevNod[1] );
4269         std::swap( nextNod[0], nextNod[1] );
4270         std::swap( isSingleNode[0], isSingleNode[1] );
4271         if ( nbSame > 0 )
4272           sames[0] = 1 - sames[0];
4273         iNotSameNode = 1 - iNotSameNode;
4274       }
4275     }
4276   }
4277
4278   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4279   if ( nbSame > 0 ) {
4280     iSameNode    = sames[ nbSame-1 ];
4281     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4282     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4283     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4284   }
4285
4286   if ( baseType == SMDSEntity_Polygon )
4287   {
4288     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4289     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4290   }
4291   else if ( baseType == SMDSEntity_Quad_Polygon )
4292   {
4293     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4294     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4295   }
4296
4297   // make new elements
4298   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4299   {
4300     // get next nodes
4301     for ( iNode = 0; iNode < nbNodes; iNode++ )
4302     {
4303       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4304       nextNod[ iNode ] = *itNN[ iNode ]++;
4305     }
4306
4307     SMDS_MeshElement* aNewElem = 0;
4308     /*if(!elem->IsPoly())*/ {
4309       switch ( baseType ) {
4310       case SMDSEntity_0D:
4311       case SMDSEntity_Node: { // sweep NODE
4312         if ( nbSame == 0 ) {
4313           if ( isSingleNode[0] )
4314             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4315           else
4316             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4317         }
4318         else
4319           return;
4320         break;
4321       }
4322       case SMDSEntity_Edge: { // sweep EDGE
4323         if ( nbDouble == 0 )
4324         {
4325           if ( nbSame == 0 ) // ---> quadrangle
4326             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4327                                       nextNod[ 1 ], nextNod[ 0 ] );
4328           else               // ---> triangle
4329             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4330                                       nextNod[ iNotSameNode ] );
4331         }
4332         else                 // ---> polygon
4333         {
4334           vector<const SMDS_MeshNode*> poly_nodes;
4335           poly_nodes.push_back( prevNod[0] );
4336           poly_nodes.push_back( prevNod[1] );
4337           if ( prevNod[1] != nextNod[1] )
4338           {
4339             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4340             poly_nodes.push_back( nextNod[1] );
4341           }
4342           if ( prevNod[0] != nextNod[0] )
4343           {
4344             poly_nodes.push_back( nextNod[0] );
4345             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4346           }
4347           switch ( poly_nodes.size() ) {
4348           case 3:
4349             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4350             break;
4351           case 4:
4352             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4353                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4354             break;
4355           default:
4356             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4357           }
4358         }
4359         break;
4360       }
4361       case SMDSEntity_Triangle: // TRIANGLE --->
4362       {
4363         if ( nbDouble > 0 ) break;
4364         if ( nbSame == 0 )       // ---> pentahedron
4365           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4366                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4367
4368         else if ( nbSame == 1 )  // ---> pyramid
4369           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4370                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4371                                        nextNod[ iSameNode ]);
4372
4373         else // 2 same nodes:       ---> tetrahedron
4374           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4375                                        nextNod[ iNotSameNode ]);
4376         break;
4377       }
4378       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4379       {
4380         if ( nbSame == 2 )
4381           return;
4382         if ( nbDouble+nbSame == 2 )
4383         {
4384           if(nbSame==0) {      // ---> quadratic quadrangle
4385             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4387           }
4388           else { //(nbSame==1) // ---> quadratic triangle
4389             if(sames[0]==2) {
4390               return; // medium node on axis
4391             }
4392             else if(sames[0]==0)
4393               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4394                                         prevNod[2], midlNod[1], nextNod[2] );
4395             else // sames[0]==1
4396               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4397                                         prevNod[2], nextNod[2], midlNod[0]);
4398           }
4399         }
4400         else if ( nbDouble == 3 )
4401         {
4402           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4403             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4404                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4405           }
4406         }
4407         else
4408           return;
4409         break;
4410       }
4411       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4412         if ( nbDouble > 0 ) break;
4413
4414         if ( nbSame == 0 )       // ---> hexahedron
4415           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4416                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4417
4418         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4419           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4420                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4421                                        nextNod[ iSameNode ]);
4422           newElems.push_back( aNewElem );
4423           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4424                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4425                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4426         }
4427         else if ( nbSame == 2 ) { // ---> pentahedron
4428           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4429             // iBeforeSame is same too
4430             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4431                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4432                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4433           else
4434             // iAfterSame is same too
4435             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4436                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4437                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4438         }
4439         break;
4440       }
4441       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4442       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4443         if ( nbDouble+nbSame != 3 ) break;
4444         if(nbSame==0) {
4445           // --->  pentahedron with 15 nodes
4446           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4447                                        nextNod[0], nextNod[1], nextNod[2],
4448                                        prevNod[3], prevNod[4], prevNod[5],
4449                                        nextNod[3], nextNod[4], nextNod[5],
4450                                        midlNod[0], midlNod[1], midlNod[2]);
4451         }
4452         else if(nbSame==1) {
4453           // --->  2d order pyramid of 13 nodes
4454           int apex = iSameNode;
4455           int i0 = ( apex + 1 ) % nbCorners;
4456           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4457           int i0a = apex + 3;
4458           int i1a = i1 + 3;
4459           int i01 = i0 + 3;
4460           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4461                                       nextNod[i0], nextNod[i1], prevNod[apex],
4462                                       prevNod[i01], midlNod[i0],
4463                                       nextNod[i01], midlNod[i1],
4464                                       prevNod[i1a], prevNod[i0a],
4465                                       nextNod[i0a], nextNod[i1a]);
4466         }
4467         else if(nbSame==2) {
4468           // --->  2d order tetrahedron of 10 nodes
4469           int n1 = iNotSameNode;
4470           int n2 = ( n1 + 1             ) % nbCorners;
4471           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4472           int n12 = n1 + 3;
4473           int n23 = n2 + 3;
4474           int n31 = n3 + 3;
4475           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4476                                        prevNod[n12], prevNod[n23], prevNod[n31],
4477                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4478         }
4479         break;
4480       }
4481       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4482         if( nbSame == 0 ) {
4483           if ( nbDouble != 4 ) break;
4484           // --->  hexahedron with 20 nodes
4485           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4486                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4487                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4488                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4489                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4490         }
4491         else if(nbSame==1) {
4492           // ---> pyramid + pentahedron - can not be created since it is needed
4493           // additional middle node at the center of face
4494           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4495           return;
4496         }
4497         else if( nbSame == 2 ) {
4498           if ( nbDouble != 2 ) break;
4499           // --->  2d order Pentahedron with 15 nodes
4500           int n1,n2,n4,n5;
4501           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4502             // iBeforeSame is same too
4503             n1 = iBeforeSame;
4504             n2 = iOpposSame;
4505             n4 = iSameNode;
4506             n5 = iAfterSame;
4507           }
4508           else {
4509             // iAfterSame is same too
4510             n1 = iSameNode;
4511             n2 = iBeforeSame;
4512             n4 = iAfterSame;
4513             n5 = iOpposSame;
4514           }
4515           int n12 = n2 + 4;
4516           int n45 = n4 + 4;
4517           int n14 = n1 + 4;
4518           int n25 = n5 + 4;
4519           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4520                                        prevNod[n4], prevNod[n5], nextNod[n5],
4521                                        prevNod[n12], midlNod[n2], nextNod[n12],
4522                                        prevNod[n45], midlNod[n5], nextNod[n45],
4523                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4524         }
4525         break;
4526       }
4527       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4528
4529         if( nbSame == 0 && nbDouble == 9 ) {
4530           // --->  tri-quadratic hexahedron with 27 nodes
4531           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4532                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4533                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4534                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4535                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4536                                        prevNod[8], // bottom center
4537                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4538                                        nextNod[8], // top center
4539                                        midlNod[8]);// elem center
4540         }
4541         else
4542         {
4543           return;
4544         }
4545         break;
4546       }
4547       case SMDSEntity_Polygon: { // sweep POLYGON
4548
4549         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4550           // --->  hexagonal prism
4551           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4552                                        prevNod[3], prevNod[4], prevNod[5],
4553                                        nextNod[0], nextNod[1], nextNod[2],
4554                                        nextNod[3], nextNod[4], nextNod[5]);
4555         }
4556         break;
4557       }
4558       case SMDSEntity_Ball:
4559         return;
4560
4561       default:
4562         break;
4563       } // switch ( baseType )
4564     } // scope
4565
4566     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4567     {
4568       if ( baseType != SMDSEntity_Polygon )
4569       {
4570         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4571         SMDS_MeshCell::applyInterlace( ind, prevNod );
4572         SMDS_MeshCell::applyInterlace( ind, nextNod );
4573         SMDS_MeshCell::applyInterlace( ind, midlNod );
4574         SMDS_MeshCell::applyInterlace( ind, itNN );
4575         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4576         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4577       }
4578       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4579       vector<int> quantities (nbNodes + 2);
4580       polyedre_nodes.clear();
4581       quantities.clear();
4582
4583       // bottom of prism
4584       for (int inode = 0; inode < nbNodes; inode++)
4585         polyedre_nodes.push_back( prevNod[inode] );
4586       quantities.push_back( nbNodes );
4587
4588       // top of prism
4589       polyedre_nodes.push_back( nextNod[0] );
4590       for (int inode = nbNodes; inode-1; --inode )
4591         polyedre_nodes.push_back( nextNod[inode-1] );
4592       quantities.push_back( nbNodes );
4593
4594       // side faces
4595       // 3--6--2
4596       // |     |
4597       // 7     5
4598       // |     |
4599       // 0--4--1
4600       const int iQuad = elem->IsQuadratic();
4601       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4602       {
4603         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4604         int inextface = (iface+1+iQuad) % nbNodes;
4605         int imid      = (iface+1) % nbNodes;
4606         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4607         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4608         polyedre_nodes.push_back( prevNod[iface] );             // 1
4609         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4610         {
4611           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4612           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4613         }
4614         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4615         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4616         {
4617           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4618           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4619         }
4620         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4621         if ( nbFaceNodes > 2 )
4622           quantities.push_back( nbFaceNodes );
4623         else // degenerated face
4624           polyedre_nodes.resize( prevNbNodes );
4625       }
4626       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4627
4628     } // try to create a polyherdal prism
4629
4630     if ( aNewElem ) {
4631       newElems.push_back( aNewElem );
4632       myLastCreatedElems.push_back(aNewElem);
4633       srcElements.push_back( elem );
4634     }
4635
4636     // set new prev nodes
4637     for ( iNode = 0; iNode < nbNodes; iNode++ )
4638       prevNod[ iNode ] = nextNod[ iNode ];
4639
4640   } // loop on steps
4641 }
4642
4643 //=======================================================================
4644 /*!
4645  * \brief Create 1D and 2D elements around swept elements
4646  * \param mapNewNodes - source nodes and ones generated from them
4647  * \param newElemsMap - source elements and ones generated from them
4648  * \param elemNewNodesMap - nodes generated from each node of each element
4649  * \param elemSet - all swept elements
4650  * \param nbSteps - number of sweeping steps
4651  * \param srcElements - to append elem for each generated element
4652  */
4653 //=======================================================================
4654
4655 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4656                                   TTElemOfElemListMap &    newElemsMap,
4657                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4658                                   TIDSortedElemSet&        elemSet,
4659                                   const int                nbSteps,
4660                                   SMESH_SequenceOfElemPtr& srcElements)
4661 {
4662   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4663   SMESHDS_Mesh* aMesh = GetMeshDS();
4664
4665   // Find nodes belonging to only one initial element - sweep them into edges.
4666
4667   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4668   for ( ; nList != mapNewNodes.end(); nList++ )
4669   {
4670     const SMDS_MeshNode* node =
4671       static_cast<const SMDS_MeshNode*>( nList->first );
4672     if ( newElemsMap.count( node ))
4673       continue; // node was extruded into edge
4674     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4675     int nbInitElems = 0;
4676     const SMDS_MeshElement* el = 0;
4677     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4678     while ( eIt->more() && nbInitElems < 2 ) {
4679       const SMDS_MeshElement* e = eIt->next();
4680       SMDSAbs_ElementType  type = e->GetType();
4681       if ( type == SMDSAbs_Volume ||
4682            type < highType ||
4683            !elemSet.count(e))
4684         continue;
4685       if ( type > highType ) {
4686         nbInitElems = 0;
4687         highType    = type;
4688       }
4689       el = e;
4690       ++nbInitElems;
4691     }
4692     if ( nbInitElems == 1 ) {
4693       bool NotCreateEdge = el && el->IsMediumNode(node);
4694       if(!NotCreateEdge) {
4695         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4696         list<const SMDS_MeshElement*> newEdges;
4697         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4698       }
4699     }
4700   }
4701
4702   // Make a ceiling for each element ie an equal element of last new nodes.
4703   // Find free links of faces - make edges and sweep them into faces.
4704
4705   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4706
4707   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4708   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4709   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4710   {
4711     const SMDS_MeshElement* elem = itElem->first;
4712     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4713
4714     if(itElem->second.size()==0) continue;
4715
4716     const bool isQuadratic = elem->IsQuadratic();
4717
4718     if ( elem->GetType() == SMDSAbs_Edge ) {
4719       // create a ceiling edge
4720       if ( !isQuadratic ) {
4721         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4722                                vecNewNodes[ 1 ]->second.back())) {
4723           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4724                                                       vecNewNodes[ 1 ]->second.back()));
4725           srcElements.push_back( elem );
4726         }
4727       }
4728       else {
4729         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4730                                vecNewNodes[ 1 ]->second.back(),
4731                                vecNewNodes[ 2 ]->second.back())) {
4732           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4733                                                       vecNewNodes[ 1 ]->second.back(),
4734                                                       vecNewNodes[ 2 ]->second.back()));
4735           srcElements.push_back( elem );
4736         }
4737       }
4738     }
4739     if ( elem->GetType() != SMDSAbs_Face )
4740       continue;
4741
4742     bool hasFreeLinks = false;
4743
4744     TIDSortedElemSet avoidSet;
4745     avoidSet.insert( elem );
4746
4747     set<const SMDS_MeshNode*> aFaceLastNodes;
4748     int iNode, nbNodes = vecNewNodes.size();
4749     if ( !isQuadratic ) {
4750       // loop on the face nodes
4751       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4752         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4753         // look for free links of the face
4754         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4755         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4756         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4757         // check if a link n1-n2 is free
4758         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4759           hasFreeLinks = true;
4760           // make a new edge and a ceiling for a new edge
4761           const SMDS_MeshElement* edge;
4762           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4763             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4764             srcElements.push_back( myLastCreatedElems.back() );
4765           }
4766           n1 = vecNewNodes[ iNode ]->second.back();
4767           n2 = vecNewNodes[ iNext ]->second.back();
4768           if ( !aMesh->FindEdge( n1, n2 )) {
4769             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4770             srcElements.push_back( edge );
4771           }
4772         }
4773       }
4774     }
4775     else { // elem is quadratic face
4776       int nbn = nbNodes/2;
4777       for ( iNode = 0; iNode < nbn; iNode++ ) {
4778         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4779         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4780         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4783         // check if a link is free
4784         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4785              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4786              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4787           hasFreeLinks = true;
4788           // make an edge and a ceiling for a new edge
4789           // find medium node
4790           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4791             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4792             srcElements.push_back( elem );
4793           }
4794           n1 = vecNewNodes[ iNode ]->second.back();
4795           n2 = vecNewNodes[ iNext ]->second.back();
4796           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4797           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4798             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4799             srcElements.push_back( elem );
4800           }
4801         }
4802       }
4803       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4804         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4805       }
4806     }
4807
4808     // sweep free links into faces
4809
4810     if ( hasFreeLinks ) {
4811       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4812       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4813
4814       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4815       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4816       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4817         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4818         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4819       }
4820       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4821         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4822         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4823       }
4824       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4825         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4826         std::advance( v, volNb );
4827         // find indices of free faces of a volume and their source edges
4828         list< int > freeInd;
4829         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4830         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4831         int iF, nbF = vTool.NbFaces();
4832         for ( iF = 0; iF < nbF; iF ++ ) {
4833           if ( vTool.IsFreeFace( iF ) &&
4834                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4835                initNodeSet != faceNodeSet) // except an initial face
4836           {
4837             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4838               continue;
4839             if ( faceNodeSet == initNodeSetNoCenter )
4840               continue;
4841             freeInd.push_back( iF );
4842             // find source edge of a free face iF
4843             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4844             vector<const SMDS_MeshNode*>::iterator lastCommom;
4845             commonNodes.resize( nbNodes, 0 );
4846             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4847                                                 initNodeSet.begin(), initNodeSet.end(),
4848                                                 commonNodes.begin());
4849             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4850               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4851             else
4852               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4853 #ifdef _DEBUG_
4854             if ( !srcEdges.back() )
4855             {
4856               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4857                    << iF << " of volume #" << vTool.ID() << endl;
4858             }
4859 #endif
4860           }
4861         }
4862         if ( freeInd.empty() )
4863           continue;
4864
4865         // create wall faces for all steps;
4866         // if such a face has been already created by sweep of edge,
4867         // assure that its orientation is OK
4868         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4869         {
4870           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4871           vTool.SetExternalNormal();
4872           const int nextShift = vTool.IsForward() ? +1 : -1;
4873           list< int >::iterator ind = freeInd.begin();
4874           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4875           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4876           {
4877             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4878             int nbn = vTool.NbFaceNodes( *ind );
4879             const SMDS_MeshElement * f = 0;
4880             if ( nbn == 3 )              ///// triangle
4881             {
4882               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4883               if ( !f ||
4884                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4885               {
4886                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4887                                                      nodes[ 1 ],
4888                                                      nodes[ 1 + nextShift ] };
4889                 if ( f )
4890                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4891                 else
4892                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4893                                                                newOrder[ 2 ] ));
4894               }
4895             }
4896             else if ( nbn == 4 )       ///// quadrangle
4897             {
4898               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4899               if ( !f ||
4900                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4901               {
4902                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4903                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4904                 if ( f )
4905                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4906                 else
4907                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4908                                                                newOrder[ 2 ], newOrder[ 3 ]));
4909               }
4910             }
4911             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4912             {
4913               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4914               if ( !f ||
4915                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4916               {
4917                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4918                                                      nodes[2],
4919                                                      nodes[2 + 2*nextShift],
4920                                                      nodes[3 - 2*nextShift],
4921                                                      nodes[3],
4922                                                      nodes[3 + 2*nextShift]};
4923                 if ( f )
4924                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4925                 else
4926                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4927                                                                newOrder[ 1 ],
4928                                                                newOrder[ 2 ],
4929                                                                newOrder[ 3 ],
4930                                                                newOrder[ 4 ],
4931                                                                newOrder[ 5 ] ));
4932               }
4933             }
4934             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4935             {
4936               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4937                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4938               if ( !f ||
4939                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4940               {
4941                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4942                                                      nodes[4 - 2*nextShift],
4943                                                      nodes[4],
4944                                                      nodes[4 + 2*nextShift],
4945                                                      nodes[1],
4946                                                      nodes[5 - 2*nextShift],
4947                                                      nodes[5],
4948                                                      nodes[5 + 2*nextShift] };
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4951                 else
4952                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4953                                                               newOrder[ 2 ], newOrder[ 3 ],
4954                                                               newOrder[ 4 ], newOrder[ 5 ],
4955                                                               newOrder[ 6 ], newOrder[ 7 ]));
4956               }
4957             }
4958             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4959             {
4960               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4961                                       SMDSAbs_Face, /*noMedium=*/false);
4962               if ( !f ||
4963                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4964               {
4965                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4966                                                      nodes[4 - 2*nextShift],
4967                                                      nodes[4],
4968                                                      nodes[4 + 2*nextShift],
4969                                                      nodes[1],
4970                                                      nodes[5 - 2*nextShift],
4971                                                      nodes[5],
4972                                                      nodes[5 + 2*nextShift],
4973                                                      nodes[8] };
4974                 if ( f )
4975                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4976                 else
4977                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978                                                               newOrder[ 2 ], newOrder[ 3 ],
4979                                                               newOrder[ 4 ], newOrder[ 5 ],
4980                                                               newOrder[ 6 ], newOrder[ 7 ],
4981                                                               newOrder[ 8 ]));
4982               }
4983             }
4984             else  //////// polygon
4985             {
4986               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4987               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4988               if ( !f ||
4989                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4990               {
4991                 if ( !vTool.IsForward() )
4992                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4993                 if ( f )
4994                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4995                 else
4996                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4997               }
4998             }
4999
5000             while ( srcElements.size() < myLastCreatedElems.size() )
5001               srcElements.push_back( *srcEdge );
5002
5003           }  // loop on free faces
5004
5005           // go to the next volume
5006           iVol = 0;
5007           while ( iVol++ < nbVolumesByStep ) v++;
5008
5009         } // loop on steps
5010       } // loop on volumes of one step
5011     } // sweep free links into faces
5012
5013     // Make a ceiling face with a normal external to a volume
5014
5015     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5016     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5017     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5018
5019     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5020       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5021       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5022     }
5023     if ( iF >= 0 )
5024     {
5025       lastVol.SetExternalNormal();
5026       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5027       const               int nbn = lastVol.NbFaceNodes( iF );
5028       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5029       if ( !hasFreeLinks ||
5030            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5031       {
5032         const vector<int>& interlace =
5033           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5034         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5035
5036         AddElement( nodeVec, anyFace.Init( elem ));
5037
5038         while ( srcElements.size() < myLastCreatedElems.size() )
5039           srcElements.push_back( elem );
5040       }
5041     }
5042   } // loop on swept elements
5043 }
5044
5045 //=======================================================================
5046 //function : RotationSweep
5047 //purpose  :
5048 //=======================================================================
5049
5050 SMESH_MeshEditor::PGroupIDs
5051 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5052                                 const gp_Ax1&      theAxis,
5053                                 const double       theAngle,
5054                                 const int          theNbSteps,
5055                                 const double       theTol,
5056                                 const bool         theMakeGroups,
5057                                 const bool         theMakeWalls)
5058 {
5059   ClearLastCreated();
5060
5061   setElemsFirst( theElemSets );
5062   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5063   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5064
5065   // source elements for each generated one
5066   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5067   srcElems.reserve( theElemSets[0].size() );
5068   srcNodes.reserve( theElemSets[1].size() );
5069
5070   gp_Trsf aTrsf;
5071   aTrsf.SetRotation( theAxis, theAngle );
5072   gp_Trsf aTrsf2;
5073   aTrsf2.SetRotation( theAxis, theAngle/2. );
5074
5075   gp_Lin aLine( theAxis );
5076   double aSqTol = theTol * theTol;
5077
5078   SMESHDS_Mesh* aMesh = GetMeshDS();
5079
5080   TNodeOfNodeListMap mapNewNodes;
5081   TElemOfVecOfNnlmiMap mapElemNewNodes;
5082   TTElemOfElemListMap newElemsMap;
5083
5084   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5085                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5086                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5087   // loop on theElemSets
5088   TIDSortedElemSet::iterator itElem;
5089   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5090   {
5091     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5092     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5093       const SMDS_MeshElement* elem = *itElem;
5094       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5095         continue;
5096       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5097       newNodesItVec.reserve( elem->NbNodes() );
5098
5099       // loop on elem nodes
5100       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5101       while ( itN->more() )
5102       {
5103         const SMDS_MeshNode* node = cast2Node( itN->next() );
5104
5105         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5106         double coord[3];
5107         aXYZ.Coord( coord[0], coord[1], coord[2] );
5108         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5109
5110         // check if a node has been already sweeped
5111         TNodeOfNodeListMapItr nIt =
5112           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5113         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5114         if ( listNewNodes.empty() )
5115         {
5116           // check if we are to create medium nodes between corner ones
5117           bool needMediumNodes = false;
5118           if ( isQuadraticMesh )
5119           {
5120             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5121             while (it->more() && !needMediumNodes )
5122             {
5123               const SMDS_MeshElement* invElem = it->next();
5124               if ( invElem != elem && !theElems.count( invElem )) continue;
5125               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5126               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5127                 needMediumNodes = true;
5128             }
5129           }
5130
5131           // make new nodes
5132           const SMDS_MeshNode * newNode = node;
5133           for ( int i = 0; i < theNbSteps; i++ ) {
5134             if ( !isOnAxis ) {
5135               if ( needMediumNodes )  // create a medium node
5136               {
5137                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5138                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5139                 myLastCreatedNodes.push_back(newNode);
5140                 srcNodes.push_back( node );
5141                 listNewNodes.push_back( newNode );
5142                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5143               }
5144               else {
5145                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5146               }
5147               // create a corner node
5148               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5149               myLastCreatedNodes.push_back(newNode);
5150               srcNodes.push_back( node );
5151               listNewNodes.push_back( newNode );
5152             }
5153             else {
5154               listNewNodes.push_back( newNode );
5155               // if ( needMediumNodes )
5156               //   listNewNodes.push_back( newNode );
5157             }
5158           }
5159         }
5160         newNodesItVec.push_back( nIt );
5161       }
5162       // make new elements
5163       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5164     }
5165   }
5166
5167   if ( theMakeWalls )
5168     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5169
5170   PGroupIDs newGroupIDs;
5171   if ( theMakeGroups )
5172     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5173
5174   return newGroupIDs;
5175 }
5176
5177 //=======================================================================
5178 //function : ExtrusParam
5179 //purpose  : standard construction
5180 //=======================================================================
5181
5182 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5183                                             const int                theNbSteps,
5184                                             const std::list<double>& theScales,
5185                                             const std::list<double>& theAngles,
5186                                             const gp_XYZ*            theBasePoint,
5187                                             const int                theFlags,
5188                                             const double             theTolerance):
5189   myDir( theStep ),
5190   myBaseP( Precision::Infinite(), 0, 0 ),
5191   myFlags( theFlags ),
5192   myTolerance( theTolerance ),
5193   myElemsToUse( NULL )
5194 {
5195   mySteps = new TColStd_HSequenceOfReal;
5196   const double stepSize = theStep.Magnitude();
5197   for (int i=1; i<=theNbSteps; i++ )
5198     mySteps->Append( stepSize );
5199
5200   if ( !theScales.empty() )
5201   {
5202     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5203       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5204
5205     // add medium scales
5206     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5207     myScales.reserve( theNbSteps * 2 );
5208     myScales.push_back( 0.5 * ( *s1 + 1. ));
5209     myScales.push_back( *s1 );
5210     for ( ; s2 != theScales.end(); s1 = s2++ )
5211     {
5212       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5213       myScales.push_back( *s2 );
5214     }
5215   }
5216
5217   if ( !theAngles.empty() )
5218   {
5219     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5220     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5221       linearAngleVariation( theNbSteps, angles );
5222
5223     // accumulate angles
5224     double angle = 0;
5225     int nbAngles = 0;
5226     std::list<double>::iterator a1 = angles.begin(), a2;
5227     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5228     {
5229       angle += *a1;
5230       *a1 = angle;
5231     }
5232     while ( nbAngles++ < theNbSteps )
5233       angles.push_back( angles.back() );
5234
5235     // add medium angles
5236     a2 = angles.begin(), a1 = a2++;
5237     myAngles.push_back( 0.5 * *a1 );
5238     myAngles.push_back( *a1 );
5239     for ( ; a2 != angles.end(); a1 = a2++ )
5240     {
5241       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5242       myAngles.push_back( *a2 );
5243     }
5244   }
5245
5246   if ( theBasePoint )
5247   {
5248     myBaseP = *theBasePoint;
5249   }
5250
5251   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5252       ( theTolerance > 0 ))
5253   {
5254     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5255   }
5256   else
5257   {
5258     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5259   }
5260 }
5261
5262 //=======================================================================
5263 //function : ExtrusParam
5264 //purpose  : steps are given explicitly
5265 //=======================================================================
5266
5267 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5268                                             Handle(TColStd_HSequenceOfReal) theSteps,
5269                                             const int                       theFlags,
5270                                             const double                    theTolerance):
5271   myDir( theDir ),
5272   mySteps( theSteps ),
5273   myFlags( theFlags ),
5274   myTolerance( theTolerance ),
5275   myElemsToUse( NULL )
5276 {
5277   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5278       ( theTolerance > 0 ))
5279   {
5280     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5281   }
5282   else
5283   {
5284     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5285   }
5286 }
5287
5288 //=======================================================================
5289 //function : ExtrusParam
5290 //purpose  : for extrusion by normal
5291 //=======================================================================
5292
5293 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5294                                             const int    theNbSteps,
5295                                             const int    theFlags,
5296                                             const int    theDim ):
5297   myDir( 1,0,0 ),
5298   mySteps( new TColStd_HSequenceOfReal ),
5299   myFlags( theFlags ),
5300   myTolerance( 0 ),
5301   myElemsToUse( NULL )
5302 {
5303   for (int i = 0; i < theNbSteps; i++ )
5304     mySteps->Append( theStepSize );
5305
5306   if ( theDim == 1 )
5307   {
5308     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5309   }
5310   else
5311   {
5312     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5313   }
5314 }
5315
5316 //=======================================================================
5317 //function : ExtrusParam
5318 //purpose  : for extrusion along path
5319 //=======================================================================
5320
5321 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5322                                             const gp_Pnt*                   theBasePoint,
5323                                             const std::list<double>&        theScales,
5324                                             const bool                      theMakeGroups )
5325   : myBaseP( Precision::Infinite(), 0, 0 ),
5326     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5327     myPathPoints( thePoints )
5328 {
5329   if ( theBasePoint )
5330   {
5331     myBaseP = theBasePoint->XYZ();
5332   }
5333
5334   if ( !theScales.empty() )
5335   {
5336     // add medium scales
5337     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5338     myScales.reserve( thePoints.size() * 2 );
5339     myScales.push_back( 0.5 * ( 1. + *s1 ));
5340     myScales.push_back( *s1 );
5341     for ( ; s2 != theScales.end(); s1 = s2++ )
5342     {
5343       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5344       myScales.push_back( *s2 );
5345     }
5346   }
5347
5348   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5349 }
5350
5351 //=======================================================================
5352 //function : ExtrusParam::SetElementsToUse
5353 //purpose  : stores elements to use for extrusion by normal, depending on
5354 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5355 //           define myBaseP for scaling
5356 //=======================================================================
5357
5358 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5359                                                       const TIDSortedElemSet& nodes )
5360 {
5361   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5362
5363   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5364   {
5365     myBaseP.SetCoord( 0.,0.,0. );
5366     TIDSortedElemSet newNodes;
5367
5368     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5369     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5370     {
5371       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5372       TIDSortedElemSet::const_iterator itElem = elements.begin();
5373       for ( ; itElem != elements.end(); itElem++ )
5374       {
5375         const SMDS_MeshElement* elem = *itElem;
5376         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5377         while ( itN->more() ) {
5378           const SMDS_MeshElement* node = itN->next();
5379           if ( newNodes.insert( node ).second )
5380             myBaseP += SMESH_NodeXYZ( node );
5381         }
5382       }
5383     }
5384     myBaseP /= newNodes.size();
5385   }
5386 }
5387
5388 //=======================================================================
5389 //function : ExtrusParam::beginStepIter
5390 //purpose  : prepare iteration on steps
5391 //=======================================================================
5392
5393 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5394 {
5395   myWithMediumNodes = withMediumNodes;
5396   myNextStep = 1;
5397   myCurSteps.clear();
5398 }
5399 //=======================================================================
5400 //function : ExtrusParam::moreSteps
5401 //purpose  : are there more steps?
5402 //=======================================================================
5403
5404 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5405 {
5406   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5407 }
5408 //=======================================================================
5409 //function : ExtrusParam::nextStep
5410 //purpose  : returns the next step
5411 //=======================================================================
5412
5413 double SMESH_MeshEditor::ExtrusParam::nextStep()
5414 {
5415   double res = 0;
5416   if ( !myCurSteps.empty() )
5417   {
5418     res = myCurSteps.back();
5419     myCurSteps.pop_back();
5420   }
5421   else if ( myNextStep <= mySteps->Length() )
5422   {
5423     myCurSteps.push_back( mySteps->Value( myNextStep ));
5424     ++myNextStep;
5425     if ( myWithMediumNodes )
5426     {
5427       myCurSteps.back() /= 2.;
5428       myCurSteps.push_back( myCurSteps.back() );
5429     }
5430     res = nextStep();
5431   }
5432   return res;
5433 }
5434
5435 //=======================================================================
5436 //function : ExtrusParam::makeNodesByDir
5437 //purpose  : create nodes for standard extrusion
5438 //=======================================================================
5439
5440 int SMESH_MeshEditor::ExtrusParam::
5441 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5442                 const SMDS_MeshNode*              srcNode,
5443                 std::list<const SMDS_MeshNode*> & newNodes,
5444                 const bool                        makeMediumNodes)
5445 {
5446   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5447
5448   int nbNodes = 0;
5449   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5450   {
5451     p += myDir.XYZ() * nextStep();
5452     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5453     newNodes.push_back( newNode );
5454   }
5455
5456   if ( !myScales.empty() || !myAngles.empty() )
5457   {
5458     gp_XYZ  center = myBaseP;
5459     gp_Ax1  ratationAxis( center, myDir );
5460     gp_Trsf rotation;
5461
5462     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5463     size_t i = !makeMediumNodes;
5464     for ( beginStepIter( makeMediumNodes );
5465           moreSteps();
5466           ++nIt, i += 1 + !makeMediumNodes )
5467     {
5468       center += myDir.XYZ() * nextStep();
5469
5470       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5471       bool moved = false;
5472       if ( i < myScales.size() )
5473       {
5474         xyz = ( myScales[i] * ( xyz - center )) + center;
5475         moved = true;
5476       }
5477       if ( !myAngles.empty() )
5478       {
5479         rotation.SetRotation( ratationAxis, myAngles[i] );
5480         rotation.Transforms( xyz );
5481         moved = true;
5482       }
5483       if ( moved )
5484         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5485       else
5486         break;
5487     }
5488   }
5489   return nbNodes;
5490 }
5491
5492 //=======================================================================
5493 //function : ExtrusParam::makeNodesByDirAndSew
5494 //purpose  : create nodes for standard extrusion with sewing
5495 //=======================================================================
5496
5497 int SMESH_MeshEditor::ExtrusParam::
5498 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5499                       const SMDS_MeshNode*              srcNode,
5500                       std::list<const SMDS_MeshNode*> & newNodes,
5501                       const bool                        makeMediumNodes)
5502 {
5503   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5504
5505   int nbNodes = 0;
5506   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5507   {
5508     P1 += myDir.XYZ() * nextStep();
5509
5510     // try to search in sequence of existing nodes
5511     // if myNodes.size()>0 we 'nave to use given sequence
5512     // else - use all nodes of mesh
5513     const SMDS_MeshNode * node = 0;
5514     if ( myNodes.Length() > 0 )
5515     {
5516       for ( int i = 1; i <= myNodes.Length(); i++ )
5517       {
5518         SMESH_NodeXYZ P2 = myNodes.Value(i);
5519         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5520         {
5521           node = myNodes.Value(i);
5522           break;
5523         }
5524       }
5525     }
5526     else
5527     {
5528       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5529       while(itn->more())
5530       {
5531         SMESH_NodeXYZ P2 = itn->next();
5532         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5533         {
5534           node = P2._node;
5535           break;
5536         }
5537       }
5538     }
5539
5540     if ( !node )
5541       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5542
5543     newNodes.push_back( node );
5544
5545   } // loop on steps
5546
5547   return nbNodes;
5548 }
5549
5550 //=======================================================================
5551 //function : ExtrusParam::makeNodesByNormal2D
5552 //purpose  : create nodes for extrusion using normals of faces
5553 //=======================================================================
5554
5555 int SMESH_MeshEditor::ExtrusParam::
5556 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5557                      const SMDS_MeshNode*              srcNode,
5558                      std::list<const SMDS_MeshNode*> & newNodes,
5559                      const bool                        makeMediumNodes)
5560 {
5561   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5562
5563   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5564
5565   // get normals to faces sharing srcNode
5566   vector< gp_XYZ > norms, baryCenters;
5567   gp_XYZ norm, avgNorm( 0,0,0 );
5568   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5569   while ( faceIt->more() )
5570   {
5571     const SMDS_MeshElement* face = faceIt->next();
5572     if ( myElemsToUse && !myElemsToUse->count( face ))
5573       continue;
5574     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5575     {
5576       norms.push_back( norm );
5577       avgNorm += norm;
5578       if ( !alongAvgNorm )
5579       {
5580         gp_XYZ bc(0,0,0);
5581         int nbN = 0;
5582         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5583           bc += SMESH_NodeXYZ( nIt->next() );
5584         baryCenters.push_back( bc / nbN );
5585       }
5586     }
5587   }
5588
5589   if ( norms.empty() ) return 0;
5590
5591   double normSize = avgNorm.Modulus();
5592   if ( normSize < std::numeric_limits<double>::min() )
5593     return 0;
5594
5595   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5596   {
5597     myDir = avgNorm;
5598     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5599   }
5600
5601   avgNorm /= normSize;
5602
5603   int nbNodes = 0;
5604   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5605   {
5606     gp_XYZ pNew = p;
5607     double stepSize = nextStep();
5608
5609     if ( norms.size() > 1 )
5610     {
5611       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5612       {
5613         // translate plane of a face
5614         baryCenters[ iF ] += norms[ iF ] * stepSize;
5615
5616         // find point of intersection of the face plane located at baryCenters[ iF ]
5617         // and avgNorm located at pNew
5618         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5619         double dot  = ( norms[ iF ] * avgNorm );
5620         if ( dot < std::numeric_limits<double>::min() )
5621           dot = stepSize * 1e-3;
5622         double step = -( norms[ iF ] * pNew + d ) / dot;
5623         pNew += step * avgNorm;
5624       }
5625     }
5626     else
5627     {
5628       pNew += stepSize * avgNorm;
5629     }
5630     p = pNew;
5631
5632     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5633     newNodes.push_back( newNode );
5634   }
5635   return nbNodes;
5636 }
5637
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByNormal1D
5640 //purpose  : create nodes for extrusion using normals of edges
5641 //=======================================================================
5642
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5645                      const SMDS_MeshNode*              srcNode,
5646                      std::list<const SMDS_MeshNode*> & newNodes,
5647                      const bool                        makeMediumNodes)
5648 {
5649   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5650   return 0;
5651 }
5652
5653 //=======================================================================
5654 //function : ExtrusParam::makeNodesAlongTrack
5655 //purpose  : create nodes for extrusion along path
5656 //=======================================================================
5657
5658 int SMESH_MeshEditor::ExtrusParam::
5659 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5660                      const SMDS_MeshNode*              srcNode,
5661                      std::list<const SMDS_MeshNode*> & newNodes,
5662                      const bool                        makeMediumNodes)
5663 {
5664   const Standard_Real aTolAng=1.e-4;
5665
5666   gp_Pnt aV0x = myBaseP;
5667   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5668
5669   const PathPoint& aPP0 = myPathPoints[0];
5670   gp_Pnt aP0x = aPP0.myPnt;
5671   gp_Dir aDT0x= aPP0.myTgt;
5672
5673   std::vector< gp_Pnt > centers;
5674   centers.reserve( NbSteps() * 2 );
5675
5676   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5677
5678   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5679   {
5680     const PathPoint&  aPP  = myPathPoints[j];
5681     const gp_Pnt&     aP1x = aPP.myPnt;
5682     const gp_Dir&    aDT1x = aPP.myTgt;
5683
5684     // Translation
5685     gp_Vec aV01x( aP0x, aP1x );
5686     aTrsf.SetTranslation( aV01x );
5687     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5688     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5689
5690     // rotation 1 [ T1,T0 ]
5691     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5692     if ( fabs( aAngleT1T0 ) > aTolAng )
5693     {
5694       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5695       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5696
5697       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5698     }
5699
5700     // rotation 2
5701     if ( aPP.myAngle != 0. )
5702     {
5703       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5704       aPN1 = aPN1.Transformed( aTrsfRot );
5705     }
5706
5707     // make new node
5708     if ( makeMediumNodes )
5709     {
5710       // create additional node
5711       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5712       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5713       newNodes.push_back( newNode );
5714
5715     }
5716     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5717     newNodes.push_back( newNode );
5718
5719     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5720     centers.push_back( aV1x );
5721
5722     aPN0 = aPN1;
5723     aP0x = aP1x;
5724     aV0x = aV1x;
5725     aDT0x = aDT1x;
5726   }
5727
5728   // scale
5729   if ( !myScales.empty() )
5730   {
5731     gp_Trsf aTrsfScale;
5732     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5733     for ( size_t i = !makeMediumNodes;
5734           i < myScales.size() && node != newNodes.end();
5735           i += ( 1 + !makeMediumNodes ), ++node )
5736     {
5737       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5738       gp_Pnt aN = SMESH_NodeXYZ( *node );
5739       gp_Pnt aP = aN.Transformed( aTrsfScale );
5740       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5741     }
5742   }
5743
5744   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5745 }
5746
5747 //=======================================================================
5748 //function : ExtrusionSweep
5749 //purpose  :
5750 //=======================================================================
5751
5752 SMESH_MeshEditor::PGroupIDs
5753 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5754                                   const gp_Vec&        theStep,
5755                                   const int            theNbSteps,
5756                                   TTElemOfElemListMap& newElemsMap,
5757                                   const int            theFlags,
5758                                   const double         theTolerance)
5759 {
5760   std::list<double> dummy;
5761   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5762                        theFlags, theTolerance );
5763   return ExtrusionSweep( theElems, aParams, newElemsMap );
5764 }
5765
5766 namespace
5767 {
5768
5769 //=======================================================================
5770 //function : getOriFactor
5771 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5772 //           edge curve orientation
5773 //=======================================================================
5774
5775   double getOriFactor( const TopoDS_Edge&   edge,
5776                        const SMDS_MeshNode* n1,
5777                        const SMDS_MeshNode* n2,
5778                        SMESH_MesherHelper&  helper)
5779   {
5780     double u1 = helper.GetNodeU( edge, n1, n2 );
5781     double u2 = helper.GetNodeU( edge, n2, n1 );
5782     return u1 < u2 ? 1. : -1.;
5783   }
5784 }
5785
5786 //=======================================================================
5787 //function : ExtrusionSweep
5788 //purpose  :
5789 //=======================================================================
5790
5791 SMESH_MeshEditor::PGroupIDs
5792 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5793                                   ExtrusParam&         theParams,
5794                                   TTElemOfElemListMap& newElemsMap)
5795 {
5796   ClearLastCreated();
5797
5798   setElemsFirst( theElemSets );
5799   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5800   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5801
5802   // source elements for each generated one
5803   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5804   srcElems.reserve( theElemSets[0].size() );
5805   srcNodes.reserve( theElemSets[1].size() );
5806
5807   const int nbSteps = theParams.NbSteps();
5808   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5809
5810   TNodeOfNodeListMap   mapNewNodes;
5811   TElemOfVecOfNnlmiMap mapElemNewNodes;
5812
5813   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5814                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5815                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5816   // loop on theElems
5817   TIDSortedElemSet::iterator itElem;
5818   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5819   {
5820     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5821     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5822     {
5823       // check element type
5824       const SMDS_MeshElement* elem = *itElem;
5825       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5826         continue;
5827
5828       const size_t nbNodes = elem->NbNodes();
5829       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5830       newNodesItVec.reserve( nbNodes );
5831
5832       // loop on elem nodes
5833       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5834       while ( itN->more() )
5835       {
5836         // check if a node has been already sweeped
5837         const SMDS_MeshNode* node = itN->next();
5838         TNodeOfNodeListMap::iterator nIt =
5839           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5840         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5841         if ( listNewNodes.empty() )
5842         {
5843           // make new nodes
5844
5845           // check if we are to create medium nodes between corner ones
5846           bool needMediumNodes = false;
5847           if ( isQuadraticMesh )
5848           {
5849             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5850             while (it->more() && !needMediumNodes )
5851             {
5852               const SMDS_MeshElement* invElem = it->next();
5853               if ( invElem != elem && !theElems.count( invElem )) continue;
5854               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5855               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5856                 needMediumNodes = true;
5857             }
5858           }
5859           // create nodes for all steps
5860           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5861           {
5862             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5863             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5864             {
5865               myLastCreatedNodes.push_back( *newNodesIt );
5866               srcNodes.push_back( node );
5867             }
5868           }
5869           else
5870           {
5871             if ( theParams.ToMakeBoundary() )
5872             {
5873               GetMeshDS()->Modified();
5874               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5875             }
5876             break; // newNodesItVec will be shorter than nbNodes
5877           }
5878         }
5879         newNodesItVec.push_back( nIt );
5880       }
5881       // make new elements
5882       if ( newNodesItVec.size() == nbNodes )
5883         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5884     }
5885   }
5886
5887   if ( theParams.ToMakeBoundary() ) {
5888     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5889   }
5890   PGroupIDs newGroupIDs;
5891   if ( theParams.ToMakeGroups() )
5892     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5893
5894   return newGroupIDs;
5895 }
5896
5897 //=======================================================================
5898 //function : ExtrusionAlongTrack
5899 //purpose  :
5900 //=======================================================================
5901 SMESH_MeshEditor::Extrusion_Error
5902 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5903                                        SMESH_Mesh*          theTrackMesh,
5904                                        SMDS_ElemIteratorPtr theTrackIterator,
5905                                        const SMDS_MeshNode* theN1,
5906                                        std::list<double>&   theAngles,
5907                                        const bool           theAngleVariation,
5908                                        std::list<double>&   theScales,
5909                                        const bool           theScaleVariation,
5910                                        const gp_Pnt*        theRefPoint,
5911                                        const bool           theMakeGroups)
5912 {
5913   ClearLastCreated();
5914
5915   // 1. Check data
5916   if ( theElements[0].empty() && theElements[1].empty() )
5917     return EXTR_NO_ELEMENTS;
5918
5919   ASSERT( theTrackMesh );
5920   if ( ! theTrackIterator || !theTrackIterator->more() )
5921     return EXTR_NO_ELEMENTS;
5922
5923   // 2. Get ordered nodes
5924   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5925   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5926   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5927   if ( branchEdges.empty() )
5928     return EXTR_PATH_NOT_EDGE;
5929
5930   if ( branchEdges.size() > 1 )
5931     return EXTR_BAD_PATH_SHAPE;
5932
5933   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5934   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5935   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5936     return EXTR_BAD_STARTING_NODE;
5937
5938   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5939   {
5940     // add medium nodes to pathNodes
5941     std::vector< const SMDS_MeshNode* >    pathNodes2;
5942     std::vector< const SMDS_MeshElement* > pathEdges2;
5943     pathNodes2.reserve( pathNodes.size() * 2 );
5944     pathEdges2.reserve( pathEdges.size() * 2 );
5945     for ( size_t i = 0; i < pathEdges.size(); ++i )
5946     {
5947       pathNodes2.push_back( pathNodes[i] );
5948       pathEdges2.push_back( pathEdges[i] );
5949       if ( pathEdges[i]->IsQuadratic() )
5950       {
5951         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5952         pathEdges2.push_back( pathEdges[i] );
5953       }
5954     }
5955     pathNodes2.push_back( pathNodes.back() );
5956     pathEdges.swap( pathEdges2 );
5957     pathNodes.swap( pathNodes2 );
5958   }
5959
5960   // 3. Get path data at pathNodes
5961
5962   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5963
5964   if ( theAngleVariation )
5965     linearAngleVariation( points.size()-1, theAngles );
5966   if ( theScaleVariation )
5967     linearScaleVariation( points.size()-1, theScales );
5968
5969   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5970   std::list<double>::iterator angle = theAngles.begin();
5971
5972   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5973
5974   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5975   std::map< int, double >::iterator id2factor;
5976   SMESH_MesherHelper pathHelper( *theTrackMesh );
5977   gp_Pnt p; gp_Vec tangent;
5978   const double tol2 = gp::Resolution() * gp::Resolution();
5979
5980   for ( size_t i = 0; i < pathNodes.size(); ++i )
5981   {
5982     ExtrusParam::PathPoint & point = points[ i ];
5983
5984     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5985
5986     if ( angle != theAngles.end() )
5987       point.myAngle = *angle++;
5988
5989     tangent.SetCoord( 0,0,0 );
5990     const int          shapeID = pathNodes[ i ]->GetShapeID();
5991     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
5992     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5993     switch ( shapeType )
5994     {
5995     case TopAbs_EDGE:
5996     {
5997       TopoDS_Edge edge = TopoDS::Edge( shape );
5998       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5999       if ( id2factor->second == 0 )
6000       {
6001         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6002         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6003       }
6004       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6005       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6006       curve->D1( u, p, tangent );
6007       tangent *= id2factor->second;
6008       break;
6009     }
6010     case TopAbs_VERTEX:
6011     {
6012       int nbEdges = 0;
6013       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6014       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6015       {
6016         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6017         for ( int di = -1; di <= 0; ++di )
6018         {
6019           size_t j = i + di;
6020           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6021           {
6022             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6023             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6024             if ( id2factor->second == 0 )
6025             {
6026               if ( j < i )
6027                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6028               else
6029                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6030             }
6031             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6032             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6033             gp_Vec du;
6034             curve->D1( u, p, du );
6035             double size2 = du.SquareMagnitude();
6036             if ( du.SquareMagnitude() > tol2 )
6037             {
6038               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6039               nbEdges++;
6040             }
6041             break;
6042           }
6043         }
6044       }
6045       if ( nbEdges > 0 )
6046         break;
6047     }
6048     default:
6049     {
6050       for ( int di = -1; di <= 1; di += 2 )
6051       {
6052         size_t j = i + di;
6053         if ( j < pathNodes.size() )
6054         {
6055           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6056           double size2 = dir.SquareMagnitude();
6057           if ( size2 > tol2 )
6058             tangent += dir.Divided( Sqrt( size2 )) * di;
6059         }
6060       }
6061     }
6062     } // switch ( shapeType )
6063
6064     if ( tangent.SquareMagnitude() < tol2 )
6065       return EXTR_CANT_GET_TANGENT;
6066
6067     point.myTgt = tangent;
6068
6069   } // loop on pathNodes
6070
6071
6072   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6073   TTElemOfElemListMap newElemsMap;
6074
6075   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6076
6077   return EXTR_OK;
6078 }
6079
6080 //=======================================================================
6081 //function : linearAngleVariation
6082 //purpose  : spread values over nbSteps
6083 //=======================================================================
6084
6085 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6086                                             list<double>& Angles)
6087 {
6088   int nbAngles = Angles.size();
6089   if( nbSteps > nbAngles && nbAngles > 0 )
6090   {
6091     vector<double> theAngles(nbAngles);
6092     theAngles.assign( Angles.begin(), Angles.end() );
6093
6094     list<double> res;
6095     double rAn2St = double( nbAngles ) / double( nbSteps );
6096     double angPrev = 0, angle;
6097     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6098     {
6099       double angCur = rAn2St * ( iSt+1 );
6100       double angCurFloor  = floor( angCur );
6101       double angPrevFloor = floor( angPrev );
6102       if ( angPrevFloor == angCurFloor )
6103         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6104       else {
6105         int iP = int( angPrevFloor );
6106         double angPrevCeil = ceil(angPrev);
6107         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6108
6109         int iC = int( angCurFloor );
6110         if ( iC < nbAngles )
6111           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6112
6113         iP = int( angPrevCeil );
6114         while ( iC-- > iP )
6115           angle += theAngles[ iC ];
6116       }
6117       res.push_back(angle);
6118       angPrev = angCur;
6119     }
6120     Angles.swap( res );
6121   }
6122 }
6123
6124 //=======================================================================
6125 //function : linearScaleVariation
6126 //purpose  : spread values over nbSteps 
6127 //=======================================================================
6128
6129 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6130                                             std::list<double>& theScales)
6131 {
6132   int nbScales = theScales.size();
6133   std::vector<double> myScales;
6134   myScales.reserve( theNbSteps );
6135   std::list<double>::const_iterator scale = theScales.begin();
6136   double prevScale = 1.0;
6137   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6138   {
6139     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6140     int    stDelta = Max( 1, iStep - myScales.size());
6141     double scDelta = ( *scale - prevScale ) / stDelta;
6142     for ( int iStep = 0; iStep < stDelta; ++iStep )
6143     {
6144       myScales.push_back( prevScale + scDelta );
6145       prevScale = myScales.back();
6146     }
6147     prevScale = *scale;
6148   }
6149   theScales.assign( myScales.begin(), myScales.end() );
6150 }
6151
6152 //================================================================================
6153 /*!
6154  * \brief Move or copy theElements applying theTrsf to their nodes
6155  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6156  *  \param theTrsf - transformation to apply
6157  *  \param theCopy - if true, create translated copies of theElems
6158  *  \param theMakeGroups - if true and theCopy, create translated groups
6159  *  \param theTargetMesh - mesh to copy translated elements into
6160  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6161  */
6162 //================================================================================
6163
6164 SMESH_MeshEditor::PGroupIDs
6165 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6166                              const gp_Trsf&     theTrsf,
6167                              const bool         theCopy,
6168                              const bool         theMakeGroups,
6169                              SMESH_Mesh*        theTargetMesh)
6170 {
6171   ClearLastCreated();
6172   myLastCreatedElems.reserve( theElems.size() );
6173
6174   bool needReverse = false;
6175   string groupPostfix;
6176   switch ( theTrsf.Form() ) {
6177   case gp_PntMirror:
6178     needReverse = true;
6179     groupPostfix = "mirrored";
6180     break;
6181   case gp_Ax1Mirror:
6182     groupPostfix = "mirrored";
6183     break;
6184   case gp_Ax2Mirror:
6185     needReverse = true;
6186     groupPostfix = "mirrored";
6187     break;
6188   case gp_Rotation:
6189     groupPostfix = "rotated";
6190     break;
6191   case gp_Translation:
6192     groupPostfix = "translated";
6193     break;
6194   case gp_Scale:
6195     groupPostfix = "scaled";
6196     break;
6197   case gp_CompoundTrsf: // different scale by axis
6198     groupPostfix = "scaled";
6199     break;
6200   default:
6201     needReverse = false;
6202     groupPostfix = "transformed";
6203   }
6204
6205   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6206   SMESHDS_Mesh* aMesh    = GetMeshDS();
6207
6208   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6209   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6210   SMESH_MeshEditor::ElemFeatures elemType;
6211
6212   // map old node to new one
6213   TNodeNodeMap nodeMap;
6214
6215   // elements sharing moved nodes; those of them which have all
6216   // nodes mirrored but are not in theElems are to be reversed
6217   TIDSortedElemSet inverseElemSet;
6218
6219   // source elements for each generated one
6220   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6221
6222   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6223   TIDSortedElemSet orphanNode;
6224
6225   if ( theElems.empty() ) // transform the whole mesh
6226   {
6227     // add all elements
6228     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6229     while ( eIt->more() ) theElems.insert( eIt->next() );
6230     // add orphan nodes
6231     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6232     while ( nIt->more() )
6233     {
6234       const SMDS_MeshNode* node = nIt->next();
6235       if ( node->NbInverseElements() == 0)
6236         orphanNode.insert( node );
6237     }
6238   }
6239
6240   // loop on elements to transform nodes : first orphan nodes then elems
6241   TIDSortedElemSet::iterator itElem;
6242   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6243   for (int i=0; i<2; i++)
6244     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6245     {
6246       const SMDS_MeshElement* elem = *itElem;
6247       if ( !elem )
6248         continue;
6249
6250       // loop on elem nodes
6251       double coord[3];
6252       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6253       while ( itN->more() )
6254       {
6255         const SMDS_MeshNode* node = cast2Node( itN->next() );
6256         // check if a node has been already transformed
6257         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6258           nodeMap.insert( make_pair ( node, node ));
6259         if ( !n2n_isnew.second )
6260           continue;
6261
6262         node->GetXYZ( coord );
6263         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6264         if ( theTargetMesh ) {
6265           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6266           n2n_isnew.first->second = newNode;
6267           myLastCreatedNodes.push_back(newNode);
6268           srcNodes.push_back( node );
6269         }
6270         else if ( theCopy ) {
6271           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6272           n2n_isnew.first->second = newNode;
6273           myLastCreatedNodes.push_back(newNode);
6274           srcNodes.push_back( node );
6275         }
6276         else {
6277           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6278           // node position on shape becomes invalid
6279           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6280             ( SMDS_SpacePosition::originSpacePosition() );
6281         }
6282
6283         // keep inverse elements
6284         if ( !theCopy && !theTargetMesh && needReverse ) {
6285           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6286           while ( invElemIt->more() ) {
6287             const SMDS_MeshElement* iel = invElemIt->next();
6288             inverseElemSet.insert( iel );
6289           }
6290         }
6291       }
6292     } // loop on elems in { &orphanNode, &theElems };
6293
6294   // either create new elements or reverse mirrored ones
6295   if ( !theCopy && !needReverse && !theTargetMesh )
6296     return PGroupIDs();
6297
6298   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6299
6300   // Replicate or reverse elements
6301
6302   std::vector<int> iForw;
6303   vector<const SMDS_MeshNode*> nodes;
6304   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6305   {
6306     const SMDS_MeshElement* elem = *itElem;
6307     if ( !elem ) continue;
6308
6309     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6310     size_t               nbNodes  = elem->NbNodes();
6311     if ( geomType == SMDSGeom_NONE ) continue; // node
6312
6313     nodes.resize( nbNodes );
6314
6315     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6316     {
6317       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6318       if ( !aPolyedre )
6319         continue;
6320       nodes.clear();
6321       bool allTransformed = true;
6322       int nbFaces = aPolyedre->NbFaces();
6323       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6324       {
6325         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6326         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6327         {
6328           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6329           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6330           if ( nodeMapIt == nodeMap.end() )
6331             allTransformed = false; // not all nodes transformed
6332           else
6333             nodes.push_back((*nodeMapIt).second);
6334         }
6335         if ( needReverse && allTransformed )
6336           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6337       }
6338       if ( !allTransformed )
6339         continue; // not all nodes transformed
6340     }
6341     else // ----------------------- the rest element types
6342     {
6343       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6344       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6345       const vector<int>&    i = needReverse ? iRev : iForw;
6346
6347       // find transformed nodes
6348       size_t iNode = 0;
6349       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6350       while ( itN->more() ) {
6351         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6352         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6353         if ( nodeMapIt == nodeMap.end() )
6354           break; // not all nodes transformed
6355         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6356       }
6357       if ( iNode != nbNodes )
6358         continue; // not all nodes transformed
6359     }
6360
6361     if ( editor ) {
6362       // copy in this or a new mesh
6363       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6364         srcElems.push_back( elem );
6365     }
6366     else {
6367       // reverse element as it was reversed by transformation
6368       if ( nbNodes > 2 )
6369         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6370     }
6371
6372   } // loop on elements
6373
6374   if ( editor && editor != this )
6375     myLastCreatedElems.swap( editor->myLastCreatedElems );
6376
6377   PGroupIDs newGroupIDs;
6378
6379   if ( ( theMakeGroups && theCopy ) ||
6380        ( theMakeGroups && theTargetMesh ) )
6381     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6382
6383   return newGroupIDs;
6384 }
6385
6386 //================================================================================
6387 /*!
6388  * \brief Make an offset mesh from a source 2D mesh
6389  *  \param [in] theElements - source faces
6390  *  \param [in] theValue - offset value
6391  *  \param [out] theTgtMesh - a mesh to add offset elements to
6392  *  \param [in] theMakeGroups - to generate groups
6393  *  \return PGroupIDs - IDs of created groups. NULL means failure
6394  */
6395 //================================================================================
6396
6397 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6398                                                       const double       theValue,
6399                                                       SMESH_Mesh*        theTgtMesh,
6400                                                       const bool         theMakeGroups,
6401                                                       const bool         theCopyElements,
6402                                                       const bool         theFixSelfIntersection)
6403 {
6404   SMESHDS_Mesh*    meshDS = GetMeshDS();
6405   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6406   SMESH_MeshEditor tgtEditor( theTgtMesh );
6407
6408   SMDS_ElemIteratorPtr eIt;
6409   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6410   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6411
6412   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6413   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6414   std::unique_ptr< SMDS_Mesh > offsetMesh
6415     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6416                                    theFixSelfIntersection,
6417                                    new2OldFaces, new2OldNodes ));
6418   if ( offsetMesh->NbElements() == 0 )
6419     return PGroupIDs(); // MakeOffset() failed
6420
6421
6422   if ( theTgtMesh == myMesh && !theCopyElements )
6423   {
6424     // clear the source elements
6425     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6426     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6427     while ( eIt->more() )
6428       meshDS->RemoveFreeElement( eIt->next(), 0 );
6429   }
6430
6431   // offsetMesh->Modified();
6432   // offsetMesh->CompactMesh(); // make IDs start from 1
6433
6434   // source elements for each generated one
6435   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6436   srcElems.reserve( new2OldFaces.size() );
6437   srcNodes.reserve( new2OldNodes.size() );
6438
6439   ClearLastCreated();
6440   myLastCreatedElems.reserve( new2OldFaces.size() );
6441   myLastCreatedNodes.reserve( new2OldNodes.size() );
6442
6443   // copy offsetMesh to theTgtMesh
6444
6445   int idShift = meshDS->MaxNodeID();
6446   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6447     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6448     {
6449 #ifndef _DEBUG_
6450       if ( n->NbInverseElements() > 0 )
6451 #endif
6452       {
6453         const SMDS_MeshNode* n2 =
6454           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6455         myLastCreatedNodes.push_back( n2 );
6456         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6457       }
6458     }
6459
6460   ElemFeatures elemType;
6461   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6462     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6463     {
6464       elemType.Init( f );
6465       elemType.myNodes.clear();
6466       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6467       {
6468         const SMDS_MeshNode* n2 = nIt->next();
6469         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6470       }
6471       tgtEditor.AddElement( elemType.myNodes, elemType );
6472       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6473     }
6474
6475   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6476
6477   PGroupIDs newGroupIDs;
6478   if ( theMakeGroups )
6479     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6480   else
6481     newGroupIDs.reset( new std::list< int > );
6482
6483   return newGroupIDs;
6484 }
6485
6486 //=======================================================================
6487 /*!
6488  * \brief Create groups of elements made during transformation
6489  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6490  *  \param elemGens - elements making corresponding myLastCreatedElems
6491  *  \param postfix - to push_back to names of new groups
6492  *  \param targetMesh - mesh to create groups in
6493  *  \param topPresent - is there are "top" elements that are created by sweeping
6494  */
6495 //=======================================================================
6496
6497 SMESH_MeshEditor::PGroupIDs
6498 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6499                                  const SMESH_SequenceOfElemPtr& elemGens,
6500                                  const std::string&             postfix,
6501                                  SMESH_Mesh*                    targetMesh,
6502                                  const bool                     topPresent)
6503 {
6504   PGroupIDs newGroupIDs( new list<int> );
6505   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6506
6507   // Sort existing groups by types and collect their names
6508
6509   // containers to store an old group and generated new ones;
6510   // 1st new group is for result elems of different type than a source one;
6511   // 2nd new group is for same type result elems ("top" group at extrusion)
6512   using boost::tuple;
6513   using boost::make_tuple;
6514   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6515   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6516   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6517   // group names
6518   set< string > groupNames;
6519
6520   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6521   if ( !groupIt->more() ) return newGroupIDs;
6522
6523   int newGroupID = mesh->GetGroupIds().back()+1;
6524   while ( groupIt->more() )
6525   {
6526     SMESH_Group * group = groupIt->next();
6527     if ( !group ) continue;
6528     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6529     if ( !groupDS || groupDS->IsEmpty() ) continue;
6530     groupNames.insert    ( group->GetName() );
6531     groupDS->SetStoreName( group->GetName() );
6532     const SMDSAbs_ElementType type = groupDS->GetType();
6533     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6534     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6535     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6536     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6537   }
6538
6539   // Loop on nodes and elements to add them in new groups
6540
6541   vector< const SMDS_MeshElement* > resultElems;
6542   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6543   {
6544     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6545     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6546     if ( gens.size() != elems.size() )
6547       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6548
6549     // loop on created elements
6550     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6551     {
6552       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6553       if ( !sourceElem ) {
6554         MESSAGE("generateGroups(): NULL source element");
6555         continue;
6556       }
6557       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6558       if ( groupsOldNew.empty() ) { // no groups of this type at all
6559         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6560           ++iElem; // skip all elements made by sourceElem
6561         continue;
6562       }
6563       // collect all elements made by the iElem-th sourceElem
6564       resultElems.clear();
6565       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6566         if ( resElem != sourceElem )
6567           resultElems.push_back( resElem );
6568       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6569         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6570           if ( resElem != sourceElem )
6571             resultElems.push_back( resElem );
6572
6573       const SMDS_MeshElement* topElem = 0;
6574       if ( isNodes ) // there must be a top element
6575       {
6576         topElem = resultElems.back();
6577         resultElems.pop_back();
6578       }
6579       else
6580       {
6581         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6582         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6583           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6584           {
6585             topElem = *resElemIt;
6586             *resElemIt = 0; // erase *resElemIt
6587             break;
6588           }
6589       }
6590       // add resultElems to groups originted from ones the sourceElem belongs to
6591       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6592       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6593       {
6594         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6595         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6596         {
6597           // fill in a new group
6598           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6599           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6600           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6601             if ( *resElemIt )
6602               newGroup.Add( *resElemIt );
6603
6604           // fill a "top" group
6605           if ( topElem )
6606           {
6607             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6608             newTopGroup.Add( topElem );
6609           }
6610         }
6611       }
6612     } // loop on created elements
6613   }// loop on nodes and elements
6614
6615   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6616
6617   list<int> topGrouIds;
6618   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6619   {
6620     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6621     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6622                                       orderedOldNewGroups[i]->get<2>() };
6623     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6624     {
6625       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6626       if ( newGroupDS->IsEmpty() )
6627       {
6628         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6629       }
6630       else
6631       {
6632         // set group type
6633         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6634
6635         // make a name
6636         const bool isTop = ( topPresent &&
6637                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6638                              is2nd );
6639
6640         string name = oldGroupDS->GetStoreName();
6641         { // remove trailing whitespaces (issue 22599)
6642           size_t size = name.size();
6643           while ( size > 1 && isspace( name[ size-1 ]))
6644             --size;
6645           if ( size != name.size() )
6646           {
6647             name.resize( size );
6648             oldGroupDS->SetStoreName( name.c_str() );
6649           }
6650         }
6651         if ( !targetMesh ) {
6652           string suffix = ( isTop ? "top": postfix.c_str() );
6653           name += "_";
6654           name += suffix;
6655           int nb = 1;
6656           while ( !groupNames.insert( name ).second ) // name exists
6657             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6658         }
6659         else if ( isTop ) {
6660           name += "_top";
6661         }
6662         newGroupDS->SetStoreName( name.c_str() );
6663
6664         // make a SMESH_Groups
6665         mesh->AddGroup( newGroupDS );
6666         if ( isTop )
6667           topGrouIds.push_back( newGroupDS->GetID() );
6668         else
6669           newGroupIDs->push_back( newGroupDS->GetID() );
6670       }
6671     }
6672   }
6673   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6674
6675   return newGroupIDs;
6676 }
6677
6678 //================================================================================
6679 /*!
6680  *  * \brief Return list of group of nodes close to each other within theTolerance
6681  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6682  *  *        an Octree algorithm
6683  *  \param [in,out] theNodes - the nodes to treat
6684  *  \param [in]     theTolerance - the tolerance
6685  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6686  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6687  *         corner and medium nodes in separate groups
6688  */
6689 //================================================================================
6690
6691 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6692                                             const double         theTolerance,
6693                                             TListOfListOfNodes & theGroupsOfNodes,
6694                                             bool                 theSeparateCornersAndMedium)
6695 {
6696   ClearLastCreated();
6697
6698   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6699        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6700        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6701     theSeparateCornersAndMedium = false;
6702
6703   TIDSortedNodeSet& corners = theNodes;
6704   TIDSortedNodeSet  medium;
6705
6706   if ( theNodes.empty() ) // get all nodes in the mesh
6707   {
6708     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6709     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6710     if ( theSeparateCornersAndMedium )
6711       while ( nIt->more() )
6712       {
6713         const SMDS_MeshNode* n = nIt->next();
6714         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6715         nodeSet->insert( nodeSet->end(), n );
6716       }
6717     else
6718       while ( nIt->more() )
6719         theNodes.insert( theNodes.end(), nIt->next() );
6720   }
6721   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6722   {
6723     TIDSortedNodeSet::iterator nIt = corners.begin();
6724     while ( nIt != corners.end() )
6725       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6726       {
6727         medium.insert( medium.end(), *nIt );
6728         corners.erase( nIt++ );
6729       }
6730       else
6731       {
6732         ++nIt;
6733       }
6734   }
6735
6736   if ( !corners.empty() )
6737     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6738   if ( !medium.empty() )
6739     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6740 }
6741
6742 //=======================================================================
6743 //function : SimplifyFace
6744 //purpose  : split a chain of nodes into several closed chains
6745 //=======================================================================
6746
6747 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6748                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6749                                     vector<int>&                         quantities) const
6750 {
6751   int nbNodes = faceNodes.size();
6752   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6753     --nbNodes;
6754   if ( nbNodes < 3 )
6755     return 0;
6756   size_t prevNbQuant = quantities.size();
6757
6758   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6759   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6760   map< const SMDS_MeshNode*, int >::iterator nInd;
6761
6762   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6763   simpleNodes.push_back( faceNodes[0] );
6764   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6765   {
6766     if ( faceNodes[ iCur ] != simpleNodes.back() )
6767     {
6768       int index = simpleNodes.size();
6769       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6770       int prevIndex = nInd->second;
6771       if ( prevIndex < index )
6772       {
6773         // a sub-loop found
6774         int loopLen = index - prevIndex;
6775         if ( loopLen > 2 )
6776         {
6777           // store the sub-loop
6778           quantities.push_back( loopLen );
6779           for ( int i = prevIndex; i < index; i++ )
6780             poly_nodes.push_back( simpleNodes[ i ]);
6781         }
6782         simpleNodes.resize( prevIndex+1 );
6783       }
6784       else
6785       {
6786         simpleNodes.push_back( faceNodes[ iCur ]);
6787       }
6788     }
6789   }
6790
6791   if ( simpleNodes.size() > 2 )
6792   {
6793     quantities.push_back( simpleNodes.size() );
6794     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6795   }
6796
6797   return quantities.size() - prevNbQuant;
6798 }
6799
6800 //=======================================================================
6801 //function : MergeNodes
6802 //purpose  : In each group, the cdr of nodes are substituted by the first one
6803 //           in all elements.
6804 //=======================================================================
6805
6806 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6807                                    const bool           theAvoidMakingHoles)
6808 {
6809   ClearLastCreated();
6810
6811   SMESHDS_Mesh* mesh = GetMeshDS();
6812
6813   TNodeNodeMap nodeNodeMap; // node to replace - new node
6814   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6815   list< int > rmElemIds, rmNodeIds;
6816   vector< ElemFeatures > newElemDefs;
6817
6818   // Fill nodeNodeMap and elems
6819
6820   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6821   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6822   {
6823     list<const SMDS_MeshNode*>& nodes = *grIt;
6824     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6825     const SMDS_MeshNode* nToKeep = *nIt;
6826     for ( ++nIt; nIt != nodes.end(); nIt++ )
6827     {
6828       const SMDS_MeshNode* nToRemove = *nIt;
6829       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6830       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6831       while ( invElemIt->more() ) {
6832         const SMDS_MeshElement* elem = invElemIt->next();
6833         elems.insert(elem);
6834       }
6835     }
6836   }
6837
6838   // Apply recursive replacements (BUG 0020185)
6839   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6840   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6841   {
6842     const SMDS_MeshNode* nToKeep = nnIt->second;
6843     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6844     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6845     {
6846       nToKeep = nnIt_i->second;
6847       nnIt->second = nToKeep;
6848       nnIt_i = nodeNodeMap.find( nToKeep );
6849     }
6850   }
6851
6852   if ( theAvoidMakingHoles )
6853   {
6854     // find elements whose topology changes
6855
6856     vector<const SMDS_MeshElement*> pbElems;
6857     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6858     for ( ; eIt != elems.end(); ++eIt )
6859     {
6860       const SMDS_MeshElement* elem = *eIt;
6861       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6862       while ( itN->more() )
6863       {
6864         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6865         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6866         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6867         {
6868           // several nodes of elem stick
6869           pbElems.push_back( elem );
6870           break;
6871         }
6872       }
6873     }
6874     // exclude from merge nodes causing spoiling element
6875     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6876     {
6877       bool nodesExcluded = false;
6878       for ( size_t i = 0; i < pbElems.size(); ++i )
6879       {
6880         size_t prevNbMergeNodes = nodeNodeMap.size();
6881         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6882              prevNbMergeNodes < nodeNodeMap.size() )
6883           nodesExcluded = true;
6884       }
6885       if ( !nodesExcluded )
6886         break;
6887     }
6888   }
6889
6890   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6891   {
6892     const SMDS_MeshNode* nToRemove = nnIt->first;
6893     const SMDS_MeshNode* nToKeep   = nnIt->second;
6894     if ( nToRemove != nToKeep )
6895     {
6896       rmNodeIds.push_back( nToRemove->GetID() );
6897       AddToSameGroups( nToKeep, nToRemove, mesh );
6898       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6899       // w/o creating node in place of merged ones.
6900       SMDS_PositionPtr pos = nToRemove->GetPosition();
6901       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6902         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6903           sm->SetIsAlwaysComputed( true );
6904     }
6905   }
6906
6907   // Change element nodes or remove an element
6908
6909   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6910   for ( ; eIt != elems.end(); eIt++ )
6911   {
6912     const SMDS_MeshElement* elem = *eIt;
6913     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6914
6915     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6916     if ( !keepElem )
6917       rmElemIds.push_back( elem->GetID() );
6918
6919     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6920     {
6921       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6922                                                & newElemDefs[i].myNodes[0],
6923                                                newElemDefs[i].myNodes.size() ))
6924       {
6925         if ( i == 0 )
6926         {
6927           newElemDefs[i].SetID( elem->GetID() );
6928           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6929           if ( !keepElem ) rmElemIds.pop_back();
6930         }
6931         else
6932         {
6933           newElemDefs[i].SetID( -1 );
6934         }
6935         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6936         if ( sm && newElem )
6937           sm->AddElement( newElem );
6938         if ( elem != newElem )
6939           ReplaceElemInGroups( elem, newElem, mesh );
6940       }
6941     }
6942   }
6943
6944   // Remove bad elements, then equal nodes (order important)
6945   Remove( rmElemIds, /*isNodes=*/false );
6946   Remove( rmNodeIds, /*isNodes=*/true );
6947
6948   return;
6949 }
6950
6951 //=======================================================================
6952 //function : applyMerge
6953 //purpose  : Compute new connectivity of an element after merging nodes
6954 //  \param [in] elems - the element
6955 //  \param [out] newElemDefs - definition(s) of result element(s)
6956 //  \param [inout] nodeNodeMap - nodes to merge
6957 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6958 //              after merging (but not degenerated), removes nodes causing
6959 //              the invalidity from \a nodeNodeMap.
6960 //  \return bool - true if the element should be removed
6961 //=======================================================================
6962
6963 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6964                                    vector< ElemFeatures >& newElemDefs,
6965                                    TNodeNodeMap&           nodeNodeMap,
6966                                    const bool              avoidMakingHoles )
6967 {
6968   bool toRemove = false; // to remove elem
6969   int nbResElems = 1;    // nb new elements
6970
6971   newElemDefs.resize(nbResElems);
6972   newElemDefs[0].Init( elem );
6973   newElemDefs[0].myNodes.clear();
6974
6975   set<const SMDS_MeshNode*> nodeSet;
6976   vector< const SMDS_MeshNode*>   curNodes;
6977   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6978   vector<int> iRepl;
6979
6980   const        int  nbNodes = elem->NbNodes();
6981   SMDSAbs_EntityType entity = elem->GetEntityType();
6982
6983   curNodes.resize( nbNodes );
6984   uniqueNodes.resize( nbNodes );
6985   iRepl.resize( nbNodes );
6986   int iUnique = 0, iCur = 0, nbRepl = 0;
6987
6988   // Get new seq of nodes
6989
6990   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991   while ( itN->more() )
6992   {
6993     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6994
6995     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6996     if ( nnIt != nodeNodeMap.end() ) {
6997       n = (*nnIt).second;
6998     }
6999     curNodes[ iCur ] = n;
7000     bool isUnique = nodeSet.insert( n ).second;
7001     if ( isUnique )
7002       uniqueNodes[ iUnique++ ] = n;
7003     else
7004       iRepl[ nbRepl++ ] = iCur;
7005     iCur++;
7006   }
7007
7008   // Analyse element topology after replacement
7009
7010   int nbUniqueNodes = nodeSet.size();
7011   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7012   {
7013     toRemove = true;
7014     nbResElems = 0;
7015
7016     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7017     {
7018       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7019       int nbCorners = nbNodes / 2;
7020       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7021       {
7022         int iNext = ( iCur + 1 ) % nbCorners;
7023         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7024         {
7025           int iMedium = iCur + nbCorners;
7026           vector< const SMDS_MeshNode* >::iterator i =
7027             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7028                        uniqueNodes.end(),
7029                        curNodes[ iMedium ]);
7030           if ( i != uniqueNodes.end() )
7031           {
7032             --nbUniqueNodes;
7033             for ( ; i+1 != uniqueNodes.end(); ++i )
7034               *i = *(i+1);
7035           }
7036         }
7037       }
7038     }
7039
7040     switch ( entity )
7041     {
7042     case SMDSEntity_Polygon:
7043     case SMDSEntity_Quad_Polygon: // Polygon
7044     {
7045       ElemFeatures* elemType = & newElemDefs[0];
7046       const bool isQuad = elemType->myIsQuad;
7047       if ( isQuad )
7048         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7049           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7050
7051       // a polygon can divide into several elements
7052       vector<const SMDS_MeshNode *> polygons_nodes;
7053       vector<int> quantities;
7054       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7055       newElemDefs.resize( nbResElems );
7056       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7057       {
7058         ElemFeatures* elemType = & newElemDefs[iface];
7059         if ( iface ) elemType->Init( elem );
7060
7061         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7062         int nbNewNodes = quantities[iface];
7063         face_nodes.assign( polygons_nodes.begin() + inode,
7064                            polygons_nodes.begin() + inode + nbNewNodes );
7065         inode += nbNewNodes;
7066         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7067         {
7068           bool isValid = ( nbNewNodes % 2 == 0 );
7069           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7070             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7071           elemType->SetQuad( isValid );
7072           if ( isValid ) // put medium nodes after corners
7073             SMDS_MeshCell::applyInterlaceRev
7074               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7075                                                     nbNewNodes ), face_nodes );
7076         }
7077         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7078       }
7079       nbUniqueNodes = newElemDefs[0].myNodes.size();
7080       break;
7081     } // Polygon
7082
7083     case SMDSEntity_Polyhedra: // Polyhedral volume
7084     {
7085       if ( nbUniqueNodes >= 4 )
7086       {
7087         // each face has to be analyzed in order to check volume validity
7088         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7089         {
7090           int nbFaces = aPolyedre->NbFaces();
7091
7092           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7093           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7094           vector<const SMDS_MeshNode *>  faceNodes;
7095           poly_nodes.clear();
7096           quantities.clear();
7097
7098           for (int iface = 1; iface <= nbFaces; iface++)
7099           {
7100             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7101             faceNodes.resize( nbFaceNodes );
7102             for (int inode = 1; inode <= nbFaceNodes; inode++)
7103             {
7104               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7105               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7106               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7107                 faceNode = (*nnIt).second;
7108               faceNodes[inode - 1] = faceNode;
7109             }
7110             SimplifyFace(faceNodes, poly_nodes, quantities);
7111           }
7112
7113           if ( quantities.size() > 3 )
7114           {
7115             // TODO: remove coincident faces
7116             nbResElems = 1;
7117             nbUniqueNodes = newElemDefs[0].myNodes.size();
7118           }
7119         }
7120       }
7121     }
7122     break;
7123
7124     // Regular elements
7125     // TODO not all the possible cases are solved. Find something more generic?
7126     case SMDSEntity_Edge: //////// EDGE
7127     case SMDSEntity_Triangle: //// TRIANGLE
7128     case SMDSEntity_Quad_Triangle:
7129     case SMDSEntity_Tetra:
7130     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7131     {
7132       break;
7133     }
7134     case SMDSEntity_Quad_Edge:
7135     {
7136       break;
7137     }
7138     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7139     {
7140       if ( nbUniqueNodes < 3 )
7141         toRemove = true;
7142       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7143         toRemove = true; // opposite nodes stick
7144       else
7145         toRemove = false;
7146       break;
7147     }
7148     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7149     {
7150       //   1    5    2
7151       //    +---+---+
7152       //    |       |
7153       //   4+       +6
7154       //    |       |
7155       //    +---+---+
7156       //   0    7    3
7157       if ( nbUniqueNodes == 6 &&
7158            iRepl[0] < 4       &&
7159            ( nbRepl == 1 || iRepl[1] >= 4 ))
7160       {
7161         toRemove = false;
7162       }
7163       break;
7164     }
7165     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7166     {
7167       //   1    5    2
7168       //    +---+---+
7169       //    |       |
7170       //   4+  8+   +6
7171       //    |       |
7172       //    +---+---+
7173       //   0    7    3
7174       if ( nbUniqueNodes == 7 &&
7175            iRepl[0] < 4       &&
7176            ( nbRepl == 1 || iRepl[1] != 8 ))
7177       {
7178         toRemove = false;
7179       }
7180       break;
7181     }
7182     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7183     {
7184       if ( nbUniqueNodes == 4 ) {
7185         // ---------------------------------> tetrahedron
7186         if ( curNodes[3] == curNodes[4] &&
7187              curNodes[3] == curNodes[5] ) {
7188           // top nodes stick
7189           toRemove = false;
7190         }
7191         else if ( curNodes[0] == curNodes[1] &&
7192                   curNodes[0] == curNodes[2] ) {
7193           // bottom nodes stick: set a top before
7194           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7195           uniqueNodes[ 0 ] = curNodes [ 5 ];
7196           uniqueNodes[ 1 ] = curNodes [ 4 ];
7197           uniqueNodes[ 2 ] = curNodes [ 3 ];
7198           toRemove = false;
7199         }
7200         else if (( curNodes[0] == curNodes[3] ) +
7201                  ( curNodes[1] == curNodes[4] ) +
7202                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7203           // a lateral face turns into a line
7204           toRemove = false;
7205         }
7206       }
7207       else if ( nbUniqueNodes == 5 ) {
7208         // PENTAHEDRON --------------------> pyramid
7209         if ( curNodes[0] == curNodes[3] )
7210         {
7211           uniqueNodes[ 0 ] = curNodes[ 1 ];
7212           uniqueNodes[ 1 ] = curNodes[ 4 ];
7213           uniqueNodes[ 2 ] = curNodes[ 5 ];
7214           uniqueNodes[ 3 ] = curNodes[ 2 ];
7215           uniqueNodes[ 4 ] = curNodes[ 0 ];
7216           toRemove = false;
7217         }
7218         if ( curNodes[1] == curNodes[4] )
7219         {
7220           uniqueNodes[ 0 ] = curNodes[ 0 ];
7221           uniqueNodes[ 1 ] = curNodes[ 2 ];
7222           uniqueNodes[ 2 ] = curNodes[ 5 ];
7223           uniqueNodes[ 3 ] = curNodes[ 3 ];
7224           uniqueNodes[ 4 ] = curNodes[ 1 ];
7225           toRemove = false;
7226         }
7227         if ( curNodes[2] == curNodes[5] )
7228         {
7229           uniqueNodes[ 0 ] = curNodes[ 0 ];
7230           uniqueNodes[ 1 ] = curNodes[ 3 ];
7231           uniqueNodes[ 2 ] = curNodes[ 4 ];
7232           uniqueNodes[ 3 ] = curNodes[ 1 ];
7233           uniqueNodes[ 4 ] = curNodes[ 2 ];
7234           toRemove = false;
7235         }
7236       }
7237       break;
7238     }
7239     case SMDSEntity_Hexa:
7240     {
7241       //////////////////////////////////// HEXAHEDRON
7242       SMDS_VolumeTool hexa (elem);
7243       hexa.SetExternalNormal();
7244       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7245         //////////////////////// HEX ---> tetrahedron
7246         for ( int iFace = 0; iFace < 6; iFace++ ) {
7247           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7248           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7249               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7250               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7251             // one face turns into a point ...
7252             int  pickInd = ind[ 0 ];
7253             int iOppFace = hexa.GetOppFaceIndex( iFace );
7254             ind = hexa.GetFaceNodesIndices( iOppFace );
7255             int nbStick = 0;
7256             uniqueNodes.clear();
7257             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7258               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7259                 nbStick++;
7260               else
7261                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7262             }
7263             if ( nbStick == 1 ) {
7264               // ... and the opposite one - into a triangle.
7265               // set a top node
7266               uniqueNodes.push_back( curNodes[ pickInd ]);
7267               toRemove = false;
7268             }
7269             break;
7270           }
7271         }
7272       }
7273       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7274         //////////////////////// HEX ---> prism
7275         int nbTria = 0, iTria[3];
7276         const int *ind; // indices of face nodes
7277         // look for triangular faces
7278         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7279           ind = hexa.GetFaceNodesIndices( iFace );
7280           TIDSortedNodeSet faceNodes;
7281           for ( iCur = 0; iCur < 4; iCur++ )
7282             faceNodes.insert( curNodes[ind[iCur]] );
7283           if ( faceNodes.size() == 3 )
7284             iTria[ nbTria++ ] = iFace;
7285         }
7286         // check if triangles are opposite
7287         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7288         {
7289           // set nodes of the bottom triangle
7290           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7291           vector<int> indB;
7292           for ( iCur = 0; iCur < 4; iCur++ )
7293             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7294               indB.push_back( ind[iCur] );
7295           if ( !hexa.IsForward() )
7296             std::swap( indB[0], indB[2] );
7297           for ( iCur = 0; iCur < 3; iCur++ )
7298             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7299           // set nodes of the top triangle
7300           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7301           for ( iCur = 0; iCur < 3; ++iCur )
7302             for ( int j = 0; j < 4; ++j )
7303               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7304               {
7305                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7306                 break;
7307               }
7308           toRemove = false;
7309           break;
7310         }
7311       }
7312       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7313         //////////////////// HEXAHEDRON ---> pyramid
7314         for ( int iFace = 0; iFace < 6; iFace++ ) {
7315           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7316           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7317               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7318               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7319             // one face turns into a point ...
7320             int iOppFace = hexa.GetOppFaceIndex( iFace );
7321             ind = hexa.GetFaceNodesIndices( iOppFace );
7322             uniqueNodes.clear();
7323             for ( iCur = 0; iCur < 4; iCur++ ) {
7324               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7325                 break;
7326               else
7327                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7328             }
7329             if ( uniqueNodes.size() == 4 ) {
7330               // ... and the opposite one is a quadrangle
7331               // set a top node
7332               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7333               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7334               toRemove = false;
7335             }
7336             break;
7337           }
7338         }
7339       }
7340
7341       if ( toRemove && nbUniqueNodes > 4 ) {
7342         ////////////////// HEXAHEDRON ---> polyhedron
7343         hexa.SetExternalNormal();
7344         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7345         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7346         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7347         quantities.reserve( 6 );     quantities.clear();
7348         for ( int iFace = 0; iFace < 6; iFace++ )
7349         {
7350           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7351           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7352                curNodes[ind[1]] == curNodes[ind[3]] )
7353           {
7354             quantities.clear();
7355             break; // opposite nodes stick
7356           }
7357           nodeSet.clear();
7358           for ( iCur = 0; iCur < 4; iCur++ )
7359           {
7360             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7361               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7362           }
7363           if ( nodeSet.size() < 3 )
7364             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7365           else
7366             quantities.push_back( nodeSet.size() );
7367         }
7368         if ( quantities.size() >= 4 )
7369         {
7370           nbResElems = 1;
7371           nbUniqueNodes = poly_nodes.size();
7372           newElemDefs[0].SetPoly(true);
7373         }
7374       }
7375       break;
7376     } // case HEXAHEDRON
7377
7378     default:
7379       toRemove = true;
7380
7381     } // switch ( entity )
7382
7383     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7384     {
7385       // erase from nodeNodeMap nodes whose merge spoils elem
7386       vector< const SMDS_MeshNode* > noMergeNodes;
7387       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7388       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7389         nodeNodeMap.erase( noMergeNodes[i] );
7390     }
7391     
7392   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7393
7394   uniqueNodes.resize( nbUniqueNodes );
7395
7396   if ( !toRemove && nbResElems == 0 )
7397     nbResElems = 1;
7398
7399   newElemDefs.resize( nbResElems );
7400
7401   return !toRemove;
7402 }
7403
7404
7405 // ========================================================
7406 // class   : ComparableElement
7407 // purpose : allow comparing elements basing on their nodes
7408 // ========================================================
7409
7410 class ComparableElement : public boost::container::flat_set< int >
7411 {
7412   typedef boost::container::flat_set< int >  int_set;
7413
7414   const SMDS_MeshElement* myElem;
7415   int                     mySumID;
7416   mutable int             myGroupID;
7417
7418 public:
7419
7420   ComparableElement( const SMDS_MeshElement* theElem ):
7421     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7422   {
7423     this->reserve( theElem->NbNodes() );
7424     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7425     {
7426       int id = nodeIt->next()->GetID();
7427       mySumID += id;
7428       this->insert( id );
7429     }
7430   }
7431
7432   const SMDS_MeshElement* GetElem() const { return myElem; }
7433
7434   int& GroupID() const { return myGroupID; }
7435   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7436
7437   ComparableElement( const ComparableElement& theSource ) // move copy
7438   {
7439     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7440     (int_set&) (*this ) = boost::move( src );
7441     myElem    = src.myElem;
7442     mySumID   = src.mySumID;
7443     myGroupID = src.myGroupID;
7444   }
7445
7446   static int HashCode(const ComparableElement& se, int limit )
7447   {
7448     return ::HashCode( se.mySumID, limit );
7449   }
7450   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7451   {
7452     return ( se1 == se2 );
7453   }
7454
7455 };
7456
7457 //=======================================================================
7458 //function : FindEqualElements
7459 //purpose  : Return list of group of elements built on the same nodes.
7460 //           Search among theElements or in the whole mesh if theElements is empty
7461 //=======================================================================
7462
7463 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7464                                           TListOfListOfElementsID & theGroupsOfElementsID )
7465 {
7466   ClearLastCreated();
7467
7468   SMDS_ElemIteratorPtr elemIt;
7469   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7470   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7471
7472   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7473   typedef std::list<int>                                          TGroupOfElems;
7474   TMapOfElements               mapOfElements;
7475   std::vector< TGroupOfElems > arrayOfGroups;
7476   TGroupOfElems                groupOfElems;
7477
7478   while ( elemIt->more() )
7479   {
7480     const SMDS_MeshElement* curElem = elemIt->next();
7481     if ( curElem->IsNull() )
7482       continue;
7483     ComparableElement      compElem = curElem;
7484     // check uniqueness
7485     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7486     if ( elemInSet.GetElem() != curElem ) // coincident elem
7487     {
7488       int& iG = elemInSet.GroupID();
7489       if ( iG < 0 )
7490       {
7491         iG = arrayOfGroups.size();
7492         arrayOfGroups.push_back( groupOfElems );
7493         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7494       }
7495       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7496     }
7497   }
7498
7499   groupOfElems.clear();
7500   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7501   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7502   {
7503     if ( groupIt->size() > 1 ) {
7504       //groupOfElems.sort(); -- theElements are sorted already
7505       theGroupsOfElementsID.emplace_back( *groupIt );
7506     }
7507   }
7508 }
7509
7510 //=======================================================================
7511 //function : MergeElements
7512 //purpose  : In each given group, substitute all elements by the first one.
7513 //=======================================================================
7514
7515 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7516 {
7517   ClearLastCreated();
7518
7519   typedef list<int> TListOfIDs;
7520   TListOfIDs rmElemIds; // IDs of elems to remove
7521
7522   SMESHDS_Mesh* aMesh = GetMeshDS();
7523
7524   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7525   while ( groupsIt != theGroupsOfElementsID.end() ) {
7526     TListOfIDs& aGroupOfElemID = *groupsIt;
7527     aGroupOfElemID.sort();
7528     int elemIDToKeep = aGroupOfElemID.front();
7529     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7530     aGroupOfElemID.pop_front();
7531     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7532     while ( idIt != aGroupOfElemID.end() ) {
7533       int elemIDToRemove = *idIt;
7534       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7535       // add the kept element in groups of removed one (PAL15188)
7536       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7537       rmElemIds.push_back( elemIDToRemove );
7538       ++idIt;
7539     }
7540     ++groupsIt;
7541   }
7542
7543   Remove( rmElemIds, false );
7544 }
7545
7546 //=======================================================================
7547 //function : MergeEqualElements
7548 //purpose  : Remove all but one of elements built on the same nodes.
7549 //=======================================================================
7550
7551 void SMESH_MeshEditor::MergeEqualElements()
7552 {
7553   TIDSortedElemSet aMeshElements; /* empty input ==
7554                                      to merge equal elements in the whole mesh */
7555   TListOfListOfElementsID aGroupsOfElementsID;
7556   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7557   MergeElements( aGroupsOfElementsID );
7558 }
7559
7560 //=======================================================================
7561 //function : findAdjacentFace
7562 //purpose  :
7563 //=======================================================================
7564
7565 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7566                                                 const SMDS_MeshNode* n2,
7567                                                 const SMDS_MeshElement* elem)
7568 {
7569   TIDSortedElemSet elemSet, avoidSet;
7570   if ( elem )
7571     avoidSet.insert ( elem );
7572   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7573 }
7574
7575 //=======================================================================
7576 //function : findSegment
7577 //purpose  : Return a mesh segment by two nodes one of which can be medium
7578 //=======================================================================
7579
7580 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7581                                            const SMDS_MeshNode* n2)
7582 {
7583   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7584   while ( it->more() )
7585   {
7586     const SMDS_MeshElement* seg = it->next();
7587     if ( seg->GetNodeIndex( n2 ) >= 0 )
7588       return seg;
7589   }
7590   return 0;
7591 }
7592
7593 //=======================================================================
7594 //function : FindFreeBorder
7595 //purpose  :
7596 //=======================================================================
7597
7598 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7599
7600 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7601                                        const SMDS_MeshNode*             theSecondNode,
7602                                        const SMDS_MeshNode*             theLastNode,
7603                                        list< const SMDS_MeshNode* > &   theNodes,
7604                                        list< const SMDS_MeshElement* >& theFaces)
7605 {
7606   if ( !theFirstNode || !theSecondNode )
7607     return false;
7608   // find border face between theFirstNode and theSecondNode
7609   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7610   if ( !curElem )
7611     return false;
7612
7613   theFaces.push_back( curElem );
7614   theNodes.push_back( theFirstNode );
7615   theNodes.push_back( theSecondNode );
7616
7617   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7618   //TIDSortedElemSet foundElems;
7619   bool needTheLast = ( theLastNode != 0 );
7620
7621   vector<const SMDS_MeshNode*> nodes;
7622   
7623   while ( nStart != theLastNode ) {
7624     if ( nStart == theFirstNode )
7625       return !needTheLast;
7626
7627     // find all free border faces sharing nStart
7628
7629     list< const SMDS_MeshElement* > curElemList;
7630     list< const SMDS_MeshNode* >    nStartList;
7631     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7632     while ( invElemIt->more() ) {
7633       const SMDS_MeshElement* e = invElemIt->next();
7634       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7635       {
7636         // get nodes
7637         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7638                       SMDS_MeshElement::iterator() );
7639         nodes.push_back( nodes[ 0 ]);
7640
7641         // check 2 links
7642         int iNode = 0, nbNodes = nodes.size() - 1;
7643         for ( iNode = 0; iNode < nbNodes; iNode++ )
7644           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7645                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7646               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7647           {
7648             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7649             curElemList.push_back( e );
7650           }
7651       }
7652     }
7653     // analyse the found
7654
7655     int nbNewBorders = curElemList.size();
7656     if ( nbNewBorders == 0 ) {
7657       // no free border furthermore
7658       return !needTheLast;
7659     }
7660     else if ( nbNewBorders == 1 ) {
7661       // one more element found
7662       nIgnore = nStart;
7663       nStart = nStartList.front();
7664       curElem = curElemList.front();
7665       theFaces.push_back( curElem );
7666       theNodes.push_back( nStart );
7667     }
7668     else {
7669       // several continuations found
7670       list< const SMDS_MeshElement* >::iterator curElemIt;
7671       list< const SMDS_MeshNode* >::iterator nStartIt;
7672       // check if one of them reached the last node
7673       if ( needTheLast ) {
7674         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7675              curElemIt!= curElemList.end();
7676              curElemIt++, nStartIt++ )
7677           if ( *nStartIt == theLastNode ) {
7678             theFaces.push_back( *curElemIt );
7679             theNodes.push_back( *nStartIt );
7680             return true;
7681           }
7682       }
7683       // find the best free border by the continuations
7684       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7685       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7686       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7687            curElemIt!= curElemList.end();
7688            curElemIt++, nStartIt++ )
7689       {
7690         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7691         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7692         // find one more free border
7693         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7694           cNL->clear();
7695           cFL->clear();
7696         }
7697         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7698           // choice: clear a worse one
7699           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7700           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7701           contNodes[ iWorse ].clear();
7702           contFaces[ iWorse ].clear();
7703         }
7704       }
7705       if ( contNodes[0].empty() && contNodes[1].empty() )
7706         return false;
7707
7708       // push_back the best free border
7709       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7710       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7711       //theNodes.pop_back(); // remove nIgnore
7712       theNodes.pop_back(); // remove nStart
7713       //theFaces.pop_back(); // remove curElem
7714       theNodes.splice( theNodes.end(), *cNL );
7715       theFaces.splice( theFaces.end(), *cFL );
7716       return true;
7717
7718     } // several continuations found
7719   } // while ( nStart != theLastNode )
7720
7721   return true;
7722 }
7723
7724 //=======================================================================
7725 //function : CheckFreeBorderNodes
7726 //purpose  : Return true if the tree nodes are on a free border
7727 //=======================================================================
7728
7729 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7730                                             const SMDS_MeshNode* theNode2,
7731                                             const SMDS_MeshNode* theNode3)
7732 {
7733   list< const SMDS_MeshNode* > nodes;
7734   list< const SMDS_MeshElement* > faces;
7735   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7736 }
7737
7738 //=======================================================================
7739 //function : SewFreeBorder
7740 //purpose  :
7741 //warning  : for border-to-side sewing theSideSecondNode is considered as
7742 //           the last side node and theSideThirdNode is not used
7743 //=======================================================================
7744
7745 SMESH_MeshEditor::Sew_Error
7746 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7747                                  const SMDS_MeshNode* theBordSecondNode,
7748                                  const SMDS_MeshNode* theBordLastNode,
7749                                  const SMDS_MeshNode* theSideFirstNode,
7750                                  const SMDS_MeshNode* theSideSecondNode,
7751                                  const SMDS_MeshNode* theSideThirdNode,
7752                                  const bool           theSideIsFreeBorder,
7753                                  const bool           toCreatePolygons,
7754                                  const bool           toCreatePolyedrs)
7755 {
7756   ClearLastCreated();
7757
7758   Sew_Error aResult = SEW_OK;
7759
7760   // ====================================
7761   //    find side nodes and elements
7762   // ====================================
7763
7764   list< const SMDS_MeshNode* >    nSide[ 2 ];
7765   list< const SMDS_MeshElement* > eSide[ 2 ];
7766   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7767   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7768
7769   // Free border 1
7770   // --------------
7771   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7772                       nSide[0], eSide[0])) {
7773     MESSAGE(" Free Border 1 not found " );
7774     aResult = SEW_BORDER1_NOT_FOUND;
7775   }
7776   if (theSideIsFreeBorder) {
7777     // Free border 2
7778     // --------------
7779     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7780                         nSide[1], eSide[1])) {
7781       MESSAGE(" Free Border 2 not found " );
7782       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7783     }
7784   }
7785   if ( aResult != SEW_OK )
7786     return aResult;
7787
7788   if (!theSideIsFreeBorder) {
7789     // Side 2
7790     // --------------
7791
7792     // -------------------------------------------------------------------------
7793     // Algo:
7794     // 1. If nodes to merge are not coincident, move nodes of the free border
7795     //    from the coord sys defined by the direction from the first to last
7796     //    nodes of the border to the correspondent sys of the side 2
7797     // 2. On the side 2, find the links most co-directed with the correspondent
7798     //    links of the free border
7799     // -------------------------------------------------------------------------
7800
7801     // 1. Since sewing may break if there are volumes to split on the side 2,
7802     //    we won't move nodes but just compute new coordinates for them
7803     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7804     TNodeXYZMap nBordXYZ;
7805     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7806     list< const SMDS_MeshNode* >::iterator nBordIt;
7807
7808     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7809     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7810     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7811     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7812     double tol2 = 1.e-8;
7813     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7814     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7815       // Need node movement.
7816
7817       // find X and Z axes to create trsf
7818       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7819       gp_Vec X = Zs ^ Zb;
7820       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7821         // Zb || Zs
7822         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7823
7824       // coord systems
7825       gp_Ax3 toBordAx( Pb1, Zb, X );
7826       gp_Ax3 fromSideAx( Ps1, Zs, X );
7827       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7828       // set trsf
7829       gp_Trsf toBordSys, fromSide2Sys;
7830       toBordSys.SetTransformation( toBordAx );
7831       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7832       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7833
7834       // move
7835       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7836         const SMDS_MeshNode* n = *nBordIt;
7837         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7838         toBordSys.Transforms( xyz );
7839         fromSide2Sys.Transforms( xyz );
7840         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7841       }
7842     }
7843     else {
7844       // just insert nodes XYZ in the nBordXYZ map
7845       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7846         const SMDS_MeshNode* n = *nBordIt;
7847         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7848       }
7849     }
7850
7851     // 2. On the side 2, find the links most co-directed with the correspondent
7852     //    links of the free border
7853
7854     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7855     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7856     sideNodes.push_back( theSideFirstNode );
7857
7858     bool hasVolumes = false;
7859     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7860     set<long> foundSideLinkIDs, checkedLinkIDs;
7861     SMDS_VolumeTool volume;
7862     //const SMDS_MeshNode* faceNodes[ 4 ];
7863
7864     const SMDS_MeshNode*    sideNode;
7865     const SMDS_MeshElement* sideElem  = 0;
7866     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7867     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7868     nBordIt = bordNodes.begin();
7869     nBordIt++;
7870     // border node position and border link direction to compare with
7871     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7872     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7873     // choose next side node by link direction or by closeness to
7874     // the current border node:
7875     bool searchByDir = ( *nBordIt != theBordLastNode );
7876     do {
7877       // find the next node on the Side 2
7878       sideNode = 0;
7879       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7880       long linkID;
7881       checkedLinkIDs.clear();
7882       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7883
7884       // loop on inverse elements of current node (prevSideNode) on the Side 2
7885       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7886       while ( invElemIt->more() )
7887       {
7888         const SMDS_MeshElement* elem = invElemIt->next();
7889         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7890         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7891         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7892         bool isVolume = volume.Set( elem );
7893         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7894         if ( isVolume ) // --volume
7895           hasVolumes = true;
7896         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7897           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7898           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7899           while ( nIt->more() ) {
7900             nodes[ iNode ] = cast2Node( nIt->next() );
7901             if ( nodes[ iNode++ ] == prevSideNode )
7902               iPrevNode = iNode - 1;
7903           }
7904           // there are 2 links to check
7905           nbNodes = 2;
7906         }
7907         else // --edge
7908           continue;
7909         // loop on links, to be precise, on the second node of links
7910         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7911           const SMDS_MeshNode* n = nodes[ iNode ];
7912           if ( isVolume ) {
7913             if ( !volume.IsLinked( n, prevSideNode ))
7914               continue;
7915           }
7916           else {
7917             if ( iNode ) // a node before prevSideNode
7918               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7919             else         // a node after prevSideNode
7920               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7921           }
7922           // check if this link was already used
7923           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7924           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7925           if (!isJustChecked &&
7926               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7927           {
7928             // test a link geometrically
7929             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7930             bool linkIsBetter = false;
7931             double dot = 0.0, dist = 0.0;
7932             if ( searchByDir ) { // choose most co-directed link
7933               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7934               linkIsBetter = ( dot > maxDot );
7935             }
7936             else { // choose link with the node closest to bordPos
7937               dist = ( nextXYZ - bordPos ).SquareModulus();
7938               linkIsBetter = ( dist < minDist );
7939             }
7940             if ( linkIsBetter ) {
7941               maxDot = dot;
7942               minDist = dist;
7943               linkID = iLink;
7944               sideNode = n;
7945               sideElem = elem;
7946             }
7947           }
7948         }
7949       } // loop on inverse elements of prevSideNode
7950
7951       if ( !sideNode ) {
7952         MESSAGE(" Can't find path by links of the Side 2 ");
7953         return SEW_BAD_SIDE_NODES;
7954       }
7955       sideNodes.push_back( sideNode );
7956       sideElems.push_back( sideElem );
7957       foundSideLinkIDs.insert ( linkID );
7958       prevSideNode = sideNode;
7959
7960       if ( *nBordIt == theBordLastNode )
7961         searchByDir = false;
7962       else {
7963         // find the next border link to compare with
7964         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7965         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7966         // move to next border node if sideNode is before forward border node (bordPos)
7967         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7968           prevBordNode = *nBordIt;
7969           nBordIt++;
7970           bordPos = nBordXYZ[ *nBordIt ];
7971           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7972           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7973         }
7974       }
7975     }
7976     while ( sideNode != theSideSecondNode );
7977
7978     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7979       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7980       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7981     }
7982   } // end nodes search on the side 2
7983
7984   // ============================
7985   // sew the border to the side 2
7986   // ============================
7987
7988   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
7989   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7990
7991   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7992   if ( toMergeConformal && toCreatePolygons )
7993   {
7994     // do not merge quadrangles if polygons are OK (IPAL0052824)
7995     eIt[0] = eSide[0].begin();
7996     eIt[1] = eSide[1].begin();
7997     bool allQuads[2] = { true, true };
7998     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7999       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8000         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8001     }
8002     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8003   }
8004
8005   TListOfListOfNodes nodeGroupsToMerge;
8006   if (( toMergeConformal ) ||
8007       ( theSideIsFreeBorder && !theSideThirdNode )) {
8008
8009     // all nodes are to be merged
8010
8011     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8012          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8013          nIt[0]++, nIt[1]++ )
8014     {
8015       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8016       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8017       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8018     }
8019   }
8020   else {
8021
8022     // insert new nodes into the border and the side to get equal nb of segments
8023
8024     // get normalized parameters of nodes on the borders
8025     vector< double > param[ 2 ];
8026     param[0].resize( maxNbNodes );
8027     param[1].resize( maxNbNodes );
8028     int iNode, iBord;
8029     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8030       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8031       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8032       const SMDS_MeshNode* nPrev = *nIt;
8033       double bordLength = 0;
8034       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8035         const SMDS_MeshNode* nCur = *nIt;
8036         gp_XYZ segment (nCur->X() - nPrev->X(),
8037                         nCur->Y() - nPrev->Y(),
8038                         nCur->Z() - nPrev->Z());
8039         double segmentLen = segment.Modulus();
8040         bordLength += segmentLen;
8041         param[ iBord ][ iNode ] = bordLength;
8042         nPrev = nCur;
8043       }
8044       // normalize within [0,1]
8045       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8046         param[ iBord ][ iNode ] /= bordLength;
8047       }
8048     }
8049
8050     // loop on border segments
8051     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8052     int i[ 2 ] = { 0, 0 };
8053     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8054     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8055
8056     // element can be split while iterating on border if it has two edges in the border
8057     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8058     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8059
8060     TElemOfNodeListMap insertMap;
8061     TElemOfNodeListMap::iterator insertMapIt;
8062     // insertMap is
8063     // key:   elem to insert nodes into
8064     // value: 2 nodes to insert between + nodes to be inserted
8065     do {
8066       bool next[ 2 ] = { false, false };
8067
8068       // find min adjacent segment length after sewing
8069       double nextParam = 10., prevParam = 0;
8070       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8071         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8072           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8073         if ( i[ iBord ] > 0 )
8074           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8075       }
8076       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8077       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8078       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8079
8080       // choose to insert or to merge nodes
8081       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8082       if ( Abs( du ) <= minSegLen * 0.2 ) {
8083         // merge
8084         // ------
8085         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8086         const SMDS_MeshNode* n0 = *nIt[0];
8087         const SMDS_MeshNode* n1 = *nIt[1];
8088         nodeGroupsToMerge.back().push_back( n1 );
8089         nodeGroupsToMerge.back().push_back( n0 );
8090         // position of node of the border changes due to merge
8091         param[ 0 ][ i[0] ] += du;
8092         // move n1 for the sake of elem shape evaluation during insertion.
8093         // n1 will be removed by MergeNodes() anyway
8094         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8095         next[0] = next[1] = true;
8096       }
8097       else {
8098         // insert
8099         // ------
8100         int intoBord = ( du < 0 ) ? 0 : 1;
8101         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8102         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8103         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8104         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8105         if ( intoBord == 1 ) {
8106           // move node of the border to be on a link of elem of the side
8107           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8108           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8109           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8110           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8111         }
8112         elemReplaceMapIt = elemReplaceMap.find( elem );
8113         if ( elemReplaceMapIt != elemReplaceMap.end() )
8114           elem = elemReplaceMapIt->second;
8115
8116         insertMapIt = insertMap.find( elem );
8117         bool  notFound = ( insertMapIt == insertMap.end() );
8118         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8119         if ( otherLink ) {
8120           // insert into another link of the same element:
8121           // 1. perform insertion into the other link of the elem
8122           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8123           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8124           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8125           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8126           // 2. perform insertion into the link of adjacent faces
8127           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8128             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8129           }
8130           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8131             InsertNodesIntoLink( seg, n12, n22, nodeList );
8132           }
8133           if (toCreatePolyedrs) {
8134             // perform insertion into the links of adjacent volumes
8135             UpdateVolumes(n12, n22, nodeList);
8136           }
8137           // 3. find an element appeared on n1 and n2 after the insertion
8138           insertMap.erase( insertMapIt );
8139           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8140           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8141           elem = elem2;
8142         }
8143         if ( notFound || otherLink ) {
8144           // add element and nodes of the side into the insertMap
8145           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8146           (*insertMapIt).second.push_back( n1 );
8147           (*insertMapIt).second.push_back( n2 );
8148         }
8149         // add node to be inserted into elem
8150         (*insertMapIt).second.push_back( nIns );
8151         next[ 1 - intoBord ] = true;
8152       }
8153
8154       // go to the next segment
8155       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8156         if ( next[ iBord ] ) {
8157           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8158             eIt[ iBord ]++;
8159           nPrev[ iBord ] = *nIt[ iBord ];
8160           nIt[ iBord ]++; i[ iBord ]++;
8161         }
8162       }
8163     }
8164     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8165
8166     // perform insertion of nodes into elements
8167
8168     for (insertMapIt = insertMap.begin();
8169          insertMapIt != insertMap.end();
8170          insertMapIt++ )
8171     {
8172       const SMDS_MeshElement* elem = (*insertMapIt).first;
8173       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8174       if ( nodeList.size() < 3 ) continue;
8175       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8176       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8177
8178       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8179
8180       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8181         InsertNodesIntoLink( seg, n1, n2, nodeList );
8182       }
8183
8184       if ( !theSideIsFreeBorder ) {
8185         // look for and insert nodes into the faces adjacent to elem
8186         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8187           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8188         }
8189       }
8190       if (toCreatePolyedrs) {
8191         // perform insertion into the links of adjacent volumes
8192         UpdateVolumes(n1, n2, nodeList);
8193       }
8194     }
8195   } // end: insert new nodes
8196
8197   MergeNodes ( nodeGroupsToMerge );
8198
8199
8200   // Remove coincident segments
8201
8202   // get new segments
8203   TIDSortedElemSet segments;
8204   SMESH_SequenceOfElemPtr newFaces;
8205   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8206   {
8207     if ( !myLastCreatedElems[i] ) continue;
8208     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8209       segments.insert( segments.end(), myLastCreatedElems[i] );
8210     else
8211       newFaces.push_back( myLastCreatedElems[i] );
8212   }
8213   // get segments adjacent to merged nodes
8214   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8215   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8216   {
8217     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8218     if ( nodes.front()->IsNull() ) continue;
8219     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8220     while ( segIt->more() )
8221       segments.insert( segIt->next() );
8222   }
8223
8224   // find coincident
8225   TListOfListOfElementsID equalGroups;
8226   if ( !segments.empty() )
8227     FindEqualElements( segments, equalGroups );
8228   if ( !equalGroups.empty() )
8229   {
8230     // remove from segments those that will be removed
8231     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8232     for ( ; itGroups != equalGroups.end(); ++itGroups )
8233     {
8234       list< int >& group = *itGroups;
8235       list< int >::iterator id = group.begin();
8236       for ( ++id; id != group.end(); ++id )
8237         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8238           segments.erase( seg );
8239     }
8240     // remove equal segments
8241     MergeElements( equalGroups );
8242
8243     // restore myLastCreatedElems
8244     myLastCreatedElems = newFaces;
8245     TIDSortedElemSet::iterator seg = segments.begin();
8246     for ( ; seg != segments.end(); ++seg )
8247       myLastCreatedElems.push_back( *seg );
8248   }
8249
8250   return aResult;
8251 }
8252
8253 //=======================================================================
8254 //function : InsertNodesIntoLink
8255 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8256 //           and theBetweenNode2 and split theElement
8257 //=======================================================================
8258
8259 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8260                                            const SMDS_MeshNode*        theBetweenNode1,
8261                                            const SMDS_MeshNode*        theBetweenNode2,
8262                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8263                                            const bool                  toCreatePoly)
8264 {
8265   if ( !theElement ) return;
8266
8267   SMESHDS_Mesh *aMesh = GetMeshDS();
8268   vector<const SMDS_MeshElement*> newElems;
8269
8270   if ( theElement->GetType() == SMDSAbs_Edge )
8271   {
8272     theNodesToInsert.push_front( theBetweenNode1 );
8273     theNodesToInsert.push_back ( theBetweenNode2 );
8274     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8275     const SMDS_MeshNode* n1 = *n;
8276     for ( ++n; n != theNodesToInsert.end(); ++n )
8277     {
8278       const SMDS_MeshNode* n2 = *n;
8279       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8280         AddToSameGroups( seg, theElement, aMesh );
8281       else
8282         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8283       n1 = n2;
8284     }
8285     theNodesToInsert.pop_front();
8286     theNodesToInsert.pop_back();
8287
8288     if ( theElement->IsQuadratic() ) // add a not split part
8289     {
8290       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8291                                           theElement->end_nodes() );
8292       int iOther = 0, nbN = nodes.size();
8293       for ( ; iOther < nbN; ++iOther )
8294         if ( nodes[iOther] != theBetweenNode1 &&
8295              nodes[iOther] != theBetweenNode2 )
8296           break;
8297       if      ( iOther == 0 )
8298       {
8299         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8300           AddToSameGroups( seg, theElement, aMesh );
8301         else
8302           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8303       }
8304       else if ( iOther == 2 )
8305       {
8306         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8307           AddToSameGroups( seg, theElement, aMesh );
8308         else
8309           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8310       }
8311     }
8312     // treat new elements
8313     for ( size_t i = 0; i < newElems.size(); ++i )
8314       if ( newElems[i] )
8315       {
8316         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8317         myLastCreatedElems.push_back( newElems[i] );
8318       }
8319     ReplaceElemInGroups( theElement, newElems, aMesh );
8320     aMesh->RemoveElement( theElement );
8321     return;
8322
8323   } // if ( theElement->GetType() == SMDSAbs_Edge )
8324
8325   const SMDS_MeshElement* theFace = theElement;
8326   if ( theFace->GetType() != SMDSAbs_Face ) return;
8327
8328   // find indices of 2 link nodes and of the rest nodes
8329   int iNode = 0, il1, il2, i3, i4;
8330   il1 = il2 = i3 = i4 = -1;
8331   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8332
8333   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8334   while ( nodeIt->more() ) {
8335     const SMDS_MeshNode* n = nodeIt->next();
8336     if ( n == theBetweenNode1 )
8337       il1 = iNode;
8338     else if ( n == theBetweenNode2 )
8339       il2 = iNode;
8340     else if ( i3 < 0 )
8341       i3 = iNode;
8342     else
8343       i4 = iNode;
8344     nodes[ iNode++ ] = n;
8345   }
8346   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8347     return ;
8348
8349   // arrange link nodes to go one after another regarding the face orientation
8350   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8351   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8352   if ( reverse ) {
8353     iNode = il1;
8354     il1 = il2;
8355     il2 = iNode;
8356     aNodesToInsert.reverse();
8357   }
8358   // check that not link nodes of a quadrangles are in good order
8359   int nbFaceNodes = theFace->NbNodes();
8360   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8361     iNode = i3;
8362     i3 = i4;
8363     i4 = iNode;
8364   }
8365
8366   if (toCreatePoly || theFace->IsPoly()) {
8367
8368     iNode = 0;
8369     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8370
8371     // add nodes of face up to first node of link
8372     bool isFLN = false;
8373     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8374     while ( nodeIt->more() && !isFLN ) {
8375       const SMDS_MeshNode* n = nodeIt->next();
8376       poly_nodes[iNode++] = n;
8377       isFLN = ( n == nodes[il1] );
8378     }
8379     // add nodes to insert
8380     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8381     for (; nIt != aNodesToInsert.end(); nIt++) {
8382       poly_nodes[iNode++] = *nIt;
8383     }
8384     // add nodes of face starting from last node of link
8385     while ( nodeIt->more() ) {
8386       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8387       poly_nodes[iNode++] = n;
8388     }
8389
8390     // make a new face
8391     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8392   }
8393
8394   else if ( !theFace->IsQuadratic() )
8395   {
8396     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8397     int nbLinkNodes = 2 + aNodesToInsert.size();
8398     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8399     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8400     linkNodes[ 0 ] = nodes[ il1 ];
8401     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8402     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8403     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8404       linkNodes[ iNode++ ] = *nIt;
8405     }
8406     // decide how to split a quadrangle: compare possible variants
8407     // and choose which of splits to be a quadrangle
8408     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8409     if ( nbFaceNodes == 3 ) {
8410       iBestQuad = nbSplits;
8411       i4 = i3;
8412     }
8413     else if ( nbFaceNodes == 4 ) {
8414       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8415       double aBestRate = DBL_MAX;
8416       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8417         i1 = 0; i2 = 1;
8418         double aBadRate = 0;
8419         // evaluate elements quality
8420         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8421           if ( iSplit == iQuad ) {
8422             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8423                                    linkNodes[ i2++ ],
8424                                    nodes[ i3 ],
8425                                    nodes[ i4 ]);
8426             aBadRate += getBadRate( &quad, aCrit );
8427           }
8428           else {
8429             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8430                                    linkNodes[ i2++ ],
8431                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8432             aBadRate += getBadRate( &tria, aCrit );
8433           }
8434         }
8435         // choice
8436         if ( aBadRate < aBestRate ) {
8437           iBestQuad = iQuad;
8438           aBestRate = aBadRate;
8439         }
8440       }
8441     }
8442
8443     // create new elements
8444     i1 = 0; i2 = 1;
8445     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8446     {
8447       if ( iSplit == iBestQuad )
8448         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8449                                             linkNodes[ i2++ ],
8450                                             nodes[ i3 ],
8451                                             nodes[ i4 ]));
8452       else
8453         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8454                                             linkNodes[ i2++ ],
8455                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8456     }
8457
8458     const SMDS_MeshNode* newNodes[ 4 ];
8459     newNodes[ 0 ] = linkNodes[ i1 ];
8460     newNodes[ 1 ] = linkNodes[ i2 ];
8461     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8462     newNodes[ 3 ] = nodes[ i4 ];
8463     if (iSplit == iBestQuad)
8464       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8465     else
8466       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8467
8468   } // end if(!theFace->IsQuadratic())
8469
8470   else { // theFace is quadratic
8471     // we have to split theFace on simple triangles and one simple quadrangle
8472     int tmp = il1/2;
8473     int nbshift = tmp*2;
8474     // shift nodes in nodes[] by nbshift
8475     int i,j;
8476     for(i=0; i<nbshift; i++) {
8477       const SMDS_MeshNode* n = nodes[0];
8478       for(j=0; j<nbFaceNodes-1; j++) {
8479         nodes[j] = nodes[j+1];
8480       }
8481       nodes[nbFaceNodes-1] = n;
8482     }
8483     il1 = il1 - nbshift;
8484     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8485     //   n0      n1     n2    n0      n1     n2
8486     //     +-----+-----+        +-----+-----+
8487     //      \         /         |           |
8488     //       \       /          |           |
8489     //      n5+     +n3       n7+           +n3
8490     //         \   /            |           |
8491     //          \ /             |           |
8492     //           +              +-----+-----+
8493     //           n4           n6      n5     n4
8494
8495     // create new elements
8496     int n1,n2,n3;
8497     if ( nbFaceNodes == 6 ) { // quadratic triangle
8498       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8499       if ( theFace->IsMediumNode(nodes[il1]) ) {
8500         // create quadrangle
8501         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8502         n1 = 1;
8503         n2 = 2;
8504         n3 = 3;
8505       }
8506       else {
8507         // create quadrangle
8508         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8509         n1 = 0;
8510         n2 = 1;
8511         n3 = 5;
8512       }
8513     }
8514     else { // nbFaceNodes==8 - quadratic quadrangle
8515       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8516       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8517       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8518       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8519         // create quadrangle
8520         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8521         n1 = 1;
8522         n2 = 2;
8523         n3 = 3;
8524       }
8525       else {
8526         // create quadrangle
8527         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8528         n1 = 0;
8529         n2 = 1;
8530         n3 = 7;
8531       }
8532     }
8533     // create needed triangles using n1,n2,n3 and inserted nodes
8534     int nbn = 2 + aNodesToInsert.size();
8535     vector<const SMDS_MeshNode*> aNodes(nbn);
8536     aNodes[0    ] = nodes[n1];
8537     aNodes[nbn-1] = nodes[n2];
8538     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8539     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8540       aNodes[iNode++] = *nIt;
8541     }
8542     for ( i = 1; i < nbn; i++ )
8543       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8544   }
8545
8546   // remove the old face
8547   for ( size_t i = 0; i < newElems.size(); ++i )
8548     if ( newElems[i] )
8549     {
8550       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8551       myLastCreatedElems.push_back( newElems[i] );
8552     }
8553   ReplaceElemInGroups( theFace, newElems, aMesh );
8554   aMesh->RemoveElement(theFace);
8555
8556 } // InsertNodesIntoLink()
8557
8558 //=======================================================================
8559 //function : UpdateVolumes
8560 //purpose  :
8561 //=======================================================================
8562
8563 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8564                                       const SMDS_MeshNode*        theBetweenNode2,
8565                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8566 {
8567   ClearLastCreated();
8568
8569   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8570   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8571     const SMDS_MeshElement* elem = invElemIt->next();
8572
8573     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8574     SMDS_VolumeTool aVolume (elem);
8575     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8576       continue;
8577
8578     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8579     int iface, nbFaces = aVolume.NbFaces();
8580     vector<const SMDS_MeshNode *> poly_nodes;
8581     vector<int> quantities (nbFaces);
8582
8583     for (iface = 0; iface < nbFaces; iface++) {
8584       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8585       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8586       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8587
8588       for (int inode = 0; inode < nbFaceNodes; inode++) {
8589         poly_nodes.push_back(faceNodes[inode]);
8590
8591         if (nbInserted == 0) {
8592           if (faceNodes[inode] == theBetweenNode1) {
8593             if (faceNodes[inode + 1] == theBetweenNode2) {
8594               nbInserted = theNodesToInsert.size();
8595
8596               // add nodes to insert
8597               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8598               for (; nIt != theNodesToInsert.end(); nIt++) {
8599                 poly_nodes.push_back(*nIt);
8600               }
8601             }
8602           }
8603           else if (faceNodes[inode] == theBetweenNode2) {
8604             if (faceNodes[inode + 1] == theBetweenNode1) {
8605               nbInserted = theNodesToInsert.size();
8606
8607               // add nodes to insert in reversed order
8608               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8609               nIt--;
8610               for (; nIt != theNodesToInsert.begin(); nIt--) {
8611                 poly_nodes.push_back(*nIt);
8612               }
8613               poly_nodes.push_back(*nIt);
8614             }
8615           }
8616           else {
8617           }
8618         }
8619       }
8620       quantities[iface] = nbFaceNodes + nbInserted;
8621     }
8622
8623     // Replace the volume
8624     SMESHDS_Mesh *aMesh = GetMeshDS();
8625
8626     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8627     {
8628       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8629       myLastCreatedElems.push_back( newElem );
8630       ReplaceElemInGroups( elem, newElem, aMesh );
8631     }
8632     aMesh->RemoveElement( elem );
8633   }
8634 }
8635
8636 namespace
8637 {
8638   //================================================================================
8639   /*!
8640    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8641    */
8642   //================================================================================
8643
8644   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8645                            vector<const SMDS_MeshNode *> & nodes,
8646                            vector<int> &                   nbNodeInFaces )
8647   {
8648     nodes.clear();
8649     nbNodeInFaces.clear();
8650     SMDS_VolumeTool vTool ( elem );
8651     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8652     {
8653       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8654       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8655       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8656     }
8657   }
8658 }
8659
8660 //=======================================================================
8661 /*!
8662  * \brief Convert elements contained in a sub-mesh to quadratic
8663  * \return int - nb of checked elements
8664  */
8665 //=======================================================================
8666
8667 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8668                                              SMESH_MesherHelper& theHelper,
8669                                              const bool          theForce3d)
8670 {
8671   //MESSAGE("convertElemToQuadratic");
8672   int nbElem = 0;
8673   if( !theSm ) return nbElem;
8674
8675   vector<int> nbNodeInFaces;
8676   vector<const SMDS_MeshNode *> nodes;
8677   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8678   while(ElemItr->more())
8679   {
8680     nbElem++;
8681     const SMDS_MeshElement* elem = ElemItr->next();
8682     if( !elem ) continue;
8683
8684     // analyse a necessity of conversion
8685     const SMDSAbs_ElementType aType = elem->GetType();
8686     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8687       continue;
8688     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8689     bool hasCentralNodes = false;
8690     if ( elem->IsQuadratic() )
8691     {
8692       bool alreadyOK;
8693       switch ( aGeomType ) {
8694       case SMDSEntity_Quad_Triangle:
8695       case SMDSEntity_Quad_Quadrangle:
8696       case SMDSEntity_Quad_Hexa:
8697       case SMDSEntity_Quad_Penta:
8698         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8699
8700       case SMDSEntity_BiQuad_Triangle:
8701       case SMDSEntity_BiQuad_Quadrangle:
8702       case SMDSEntity_TriQuad_Hexa:
8703       case SMDSEntity_BiQuad_Penta:
8704         alreadyOK = theHelper.GetIsBiQuadratic();
8705         hasCentralNodes = true;
8706         break;
8707       default:
8708         alreadyOK = true;
8709       }
8710       // take into account already present medium nodes
8711       switch ( aType ) {
8712       case SMDSAbs_Volume:
8713         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8714       case SMDSAbs_Face:
8715         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8716       case SMDSAbs_Edge:
8717         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8718       default:;
8719       }
8720       if ( alreadyOK )
8721         continue;
8722     }
8723     // get elem data needed to re-create it
8724     //
8725     const int id      = elem->GetID();
8726     const int nbNodes = elem->NbCornerNodes();
8727     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8728     if ( aGeomType == SMDSEntity_Polyhedra )
8729       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8730     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8731       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8732
8733     // remove a linear element
8734     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8735
8736     // remove central nodes of biquadratic elements (biquad->quad conversion)
8737     if ( hasCentralNodes )
8738       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8739         if ( nodes[i]->NbInverseElements() == 0 )
8740           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8741
8742     const SMDS_MeshElement* NewElem = 0;
8743
8744     switch( aType )
8745     {
8746     case SMDSAbs_Edge :
8747     {
8748       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8749       break;
8750     }
8751     case SMDSAbs_Face :
8752     {
8753       switch(nbNodes)
8754       {
8755       case 3:
8756         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8757         break;
8758       case 4:
8759         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8760         break;
8761       default:
8762         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8763       }
8764       break;
8765     }
8766     case SMDSAbs_Volume :
8767     {
8768       switch( aGeomType )
8769       {
8770       case SMDSEntity_Tetra:
8771         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8772         break;
8773       case SMDSEntity_Pyramid:
8774         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8775         break;
8776       case SMDSEntity_Penta:
8777       case SMDSEntity_Quad_Penta:
8778       case SMDSEntity_BiQuad_Penta:
8779         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8780         break;
8781       case SMDSEntity_Hexa:
8782       case SMDSEntity_Quad_Hexa:
8783       case SMDSEntity_TriQuad_Hexa:
8784         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8785                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8786         break;
8787       case SMDSEntity_Hexagonal_Prism:
8788       default:
8789         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8790       }
8791       break;
8792     }
8793     default :
8794       continue;
8795     }
8796     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8797     if( NewElem && NewElem->getshapeId() < 1 )
8798       theSm->AddElement( NewElem );
8799   }
8800   return nbElem;
8801 }
8802 //=======================================================================
8803 //function : ConvertToQuadratic
8804 //purpose  :
8805 //=======================================================================
8806
8807 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8808 {
8809   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8810   SMESHDS_Mesh* meshDS = GetMeshDS();
8811
8812   SMESH_MesherHelper aHelper(*myMesh);
8813
8814   aHelper.SetIsQuadratic( true );
8815   aHelper.SetIsBiQuadratic( theToBiQuad );
8816   aHelper.SetElementsOnShape(true);
8817   aHelper.ToFixNodeParameters( true );
8818
8819   // convert elements assigned to sub-meshes
8820   int nbCheckedElems = 0;
8821   if ( myMesh->HasShapeToMesh() )
8822   {
8823     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8824     {
8825       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8826       while ( smIt->more() ) {
8827         SMESH_subMesh* sm = smIt->next();
8828         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8829           aHelper.SetSubShape( sm->GetSubShape() );
8830           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8831         }
8832       }
8833     }
8834   }
8835
8836   // convert elements NOT assigned to sub-meshes
8837   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8838   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8839   {
8840     aHelper.SetElementsOnShape(false);
8841     SMESHDS_SubMesh *smDS = 0;
8842
8843     // convert edges
8844     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8845     while( aEdgeItr->more() )
8846     {
8847       const SMDS_MeshEdge* edge = aEdgeItr->next();
8848       if ( !edge->IsQuadratic() )
8849       {
8850         int                  id = edge->GetID();
8851         const SMDS_MeshNode* n1 = edge->GetNode(0);
8852         const SMDS_MeshNode* n2 = edge->GetNode(1);
8853
8854         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8855
8856         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8857         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8858       }
8859       else
8860       {
8861         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8862       }
8863     }
8864
8865     // convert faces
8866     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8867     while( aFaceItr->more() )
8868     {
8869       const SMDS_MeshFace* face = aFaceItr->next();
8870       if ( !face ) continue;
8871       
8872       const SMDSAbs_EntityType type = face->GetEntityType();
8873       bool alreadyOK;
8874       switch( type )
8875       {
8876       case SMDSEntity_Quad_Triangle:
8877       case SMDSEntity_Quad_Quadrangle:
8878         alreadyOK = !theToBiQuad;
8879         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8880         break;
8881       case SMDSEntity_BiQuad_Triangle:
8882       case SMDSEntity_BiQuad_Quadrangle:
8883         alreadyOK = theToBiQuad;
8884         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8885         break;
8886       default: alreadyOK = false;
8887       }
8888       if ( alreadyOK )
8889         continue;
8890
8891       const int id = face->GetID();
8892       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8893
8894       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8895
8896       SMDS_MeshFace * NewFace = 0;
8897       switch( type )
8898       {
8899       case SMDSEntity_Triangle:
8900       case SMDSEntity_Quad_Triangle:
8901       case SMDSEntity_BiQuad_Triangle:
8902         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8903         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8904           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8905         break;
8906
8907       case SMDSEntity_Quadrangle:
8908       case SMDSEntity_Quad_Quadrangle:
8909       case SMDSEntity_BiQuad_Quadrangle:
8910         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8911         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8912           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8913         break;
8914
8915       default:;
8916         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8917       }
8918       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8919     }
8920
8921     // convert volumes
8922     vector<int> nbNodeInFaces;
8923     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8924     while(aVolumeItr->more())
8925     {
8926       const SMDS_MeshVolume* volume = aVolumeItr->next();
8927       if ( !volume ) continue;
8928
8929       const SMDSAbs_EntityType type = volume->GetEntityType();
8930       if ( volume->IsQuadratic() )
8931       {
8932         bool alreadyOK;
8933         switch ( type )
8934         {
8935         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8936         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8937         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8938         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8939         default:                      alreadyOK = true;
8940         }
8941         if ( alreadyOK )
8942         {
8943           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8944           continue;
8945         }
8946       }
8947       const int id = volume->GetID();
8948       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8949       if ( type == SMDSEntity_Polyhedra )
8950         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8951       else if ( type == SMDSEntity_Hexagonal_Prism )
8952         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8953
8954       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8955
8956       SMDS_MeshVolume * NewVolume = 0;
8957       switch ( type )
8958       {
8959       case SMDSEntity_Tetra:
8960         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8961         break;
8962       case SMDSEntity_Hexa:
8963       case SMDSEntity_Quad_Hexa:
8964       case SMDSEntity_TriQuad_Hexa:
8965         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8966                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8967         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8968           if ( nodes[i]->NbInverseElements() == 0 )
8969             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8970         break;
8971       case SMDSEntity_Pyramid:
8972         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8973                                       nodes[3], nodes[4], id, theForce3d);
8974         break;
8975       case SMDSEntity_Penta:
8976       case SMDSEntity_Quad_Penta:
8977       case SMDSEntity_BiQuad_Penta:
8978         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8979                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8980         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8981           if ( nodes[i]->NbInverseElements() == 0 )
8982             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8983         break;
8984       case SMDSEntity_Hexagonal_Prism:
8985       default:
8986         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8987       }
8988       ReplaceElemInGroups(volume, NewVolume, meshDS);
8989     }
8990   }
8991
8992   if ( !theForce3d )
8993   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8994     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8995     // aHelper.FixQuadraticElements(myError);
8996     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8997   }
8998 }
8999
9000 //================================================================================
9001 /*!
9002  * \brief Makes given elements quadratic
9003  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9004  *  \param theElements - elements to make quadratic
9005  */
9006 //================================================================================
9007
9008 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9009                                           TIDSortedElemSet& theElements,
9010                                           const bool        theToBiQuad)
9011 {
9012   if ( theElements.empty() ) return;
9013
9014   // we believe that all theElements are of the same type
9015   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9016
9017   // get all nodes shared by theElements
9018   TIDSortedNodeSet allNodes;
9019   TIDSortedElemSet::iterator eIt = theElements.begin();
9020   for ( ; eIt != theElements.end(); ++eIt )
9021     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9022
9023   // complete theElements with elements of lower dim whose all nodes are in allNodes
9024
9025   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9026   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9027   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9028   for ( ; nIt != allNodes.end(); ++nIt )
9029   {
9030     const SMDS_MeshNode* n = *nIt;
9031     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9032     while ( invIt->more() )
9033     {
9034       const SMDS_MeshElement*      e = invIt->next();
9035       const SMDSAbs_ElementType type = e->GetType();
9036       if ( e->IsQuadratic() )
9037       {
9038         quadAdjacentElems[ type ].insert( e );
9039
9040         bool alreadyOK;
9041         switch ( e->GetEntityType() ) {
9042         case SMDSEntity_Quad_Triangle:
9043         case SMDSEntity_Quad_Quadrangle:
9044         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9045         case SMDSEntity_BiQuad_Triangle:
9046         case SMDSEntity_BiQuad_Quadrangle:
9047         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9048         default:                           alreadyOK = true;
9049         }
9050         if ( alreadyOK )
9051           continue;
9052       }
9053       if ( type >= elemType )
9054         continue; // same type or more complex linear element
9055
9056       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9057         continue; // e is already checked
9058
9059       // check nodes
9060       bool allIn = true;
9061       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9062       while ( nodeIt->more() && allIn )
9063         allIn = allNodes.count( nodeIt->next() );
9064       if ( allIn )
9065         theElements.insert(e );
9066     }
9067   }
9068
9069   SMESH_MesherHelper helper(*myMesh);
9070   helper.SetIsQuadratic( true );
9071   helper.SetIsBiQuadratic( theToBiQuad );
9072
9073   // add links of quadratic adjacent elements to the helper
9074
9075   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9076     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9077           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9078     {
9079       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9080     }
9081   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9082     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9083           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9084     {
9085       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9086     }
9087   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9088     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9089           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9090     {
9091       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9092     }
9093
9094   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9095
9096   SMESHDS_Mesh*  meshDS = GetMeshDS();
9097   SMESHDS_SubMesh* smDS = 0;
9098   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9099   {
9100     const SMDS_MeshElement* elem = *eIt;
9101
9102     bool alreadyOK;
9103     int nbCentralNodes = 0;
9104     switch ( elem->GetEntityType() ) {
9105       // linear convertible
9106     case SMDSEntity_Edge:
9107     case SMDSEntity_Triangle:
9108     case SMDSEntity_Quadrangle:
9109     case SMDSEntity_Tetra:
9110     case SMDSEntity_Pyramid:
9111     case SMDSEntity_Hexa:
9112     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9113       // quadratic that can become bi-quadratic
9114     case SMDSEntity_Quad_Triangle:
9115     case SMDSEntity_Quad_Quadrangle:
9116     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9117       // bi-quadratic
9118     case SMDSEntity_BiQuad_Triangle:
9119     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9120     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9121       // the rest
9122     default:                           alreadyOK = true;
9123     }
9124     if ( alreadyOK ) continue;
9125
9126     const SMDSAbs_ElementType type = elem->GetType();
9127     const int                   id = elem->GetID();
9128     const int              nbNodes = elem->NbCornerNodes();
9129     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9130
9131     helper.SetSubShape( elem->getshapeId() );
9132
9133     if ( !smDS || !smDS->Contains( elem ))
9134       smDS = meshDS->MeshElements( elem->getshapeId() );
9135     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9136
9137     SMDS_MeshElement * newElem = 0;
9138     switch( nbNodes )
9139     {
9140     case 4: // cases for most frequently used element types go first (for optimization)
9141       if ( type == SMDSAbs_Volume )
9142         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9143       else
9144         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9145       break;
9146     case 8:
9147       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9148                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9149       break;
9150     case 3:
9151       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9152       break;
9153     case 2:
9154       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9155       break;
9156     case 5:
9157       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9158                                  nodes[4], id, theForce3d);
9159       break;
9160     case 6:
9161       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9162                                  nodes[4], nodes[5], id, theForce3d);
9163       break;
9164     default:;
9165     }
9166     ReplaceElemInGroups( elem, newElem, meshDS);
9167     if( newElem && smDS )
9168       smDS->AddElement( newElem );
9169
9170     // remove central nodes
9171     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9172       if ( nodes[i]->NbInverseElements() == 0 )
9173         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9174
9175   } // loop on theElements
9176
9177   if ( !theForce3d )
9178   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9179     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9180     // helper.FixQuadraticElements( myError );
9181     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9182   }
9183 }
9184
9185 //=======================================================================
9186 /*!
9187  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9188  * \return int - nb of checked elements
9189  */
9190 //=======================================================================
9191
9192 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9193                                      SMDS_ElemIteratorPtr theItr,
9194                                      const int            theShapeID)
9195 {
9196   int nbElem = 0;
9197   SMESHDS_Mesh* meshDS = GetMeshDS();
9198   ElemFeatures elemType;
9199   vector<const SMDS_MeshNode *> nodes;
9200
9201   while( theItr->more() )
9202   {
9203     const SMDS_MeshElement* elem = theItr->next();
9204     nbElem++;
9205     if( elem && elem->IsQuadratic())
9206     {
9207       // get elem data
9208       int nbCornerNodes = elem->NbCornerNodes();
9209       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9210
9211       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9212
9213       //remove a quadratic element
9214       if ( !theSm || !theSm->Contains( elem ))
9215         theSm = meshDS->MeshElements( elem->getshapeId() );
9216       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9217
9218       // remove medium nodes
9219       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9220         if ( nodes[i]->NbInverseElements() == 0 )
9221           meshDS->RemoveFreeNode( nodes[i], theSm );
9222
9223       // add a linear element
9224       nodes.resize( nbCornerNodes );
9225       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9226       ReplaceElemInGroups(elem, newElem, meshDS);
9227       if( theSm && newElem )
9228         theSm->AddElement( newElem );
9229     }
9230   }
9231   return nbElem;
9232 }
9233
9234 //=======================================================================
9235 //function : ConvertFromQuadratic
9236 //purpose  :
9237 //=======================================================================
9238
9239 bool SMESH_MeshEditor::ConvertFromQuadratic()
9240 {
9241   int nbCheckedElems = 0;
9242   if ( myMesh->HasShapeToMesh() )
9243   {
9244     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9245     {
9246       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9247       while ( smIt->more() ) {
9248         SMESH_subMesh* sm = smIt->next();
9249         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9250           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9251       }
9252     }
9253   }
9254
9255   int totalNbElems =
9256     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9257   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9258   {
9259     SMESHDS_SubMesh *aSM = 0;
9260     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9261   }
9262
9263   return true;
9264 }
9265
9266 namespace
9267 {
9268   //================================================================================
9269   /*!
9270    * \brief Return true if all medium nodes of the element are in the node set
9271    */
9272   //================================================================================
9273
9274   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9275   {
9276     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9277       if ( !nodeSet.count( elem->GetNode(i) ))
9278         return false;
9279     return true;
9280   }
9281 }
9282
9283 //================================================================================
9284 /*!
9285  * \brief Makes given elements linear
9286  */
9287 //================================================================================
9288
9289 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9290 {
9291   if ( theElements.empty() ) return;
9292
9293   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9294   set<int> mediumNodeIDs;
9295   TIDSortedElemSet::iterator eIt = theElements.begin();
9296   for ( ; eIt != theElements.end(); ++eIt )
9297   {
9298     const SMDS_MeshElement* e = *eIt;
9299     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9300       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9301   }
9302
9303   // replace given elements by linear ones
9304   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9305   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9306
9307   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9308   // except those elements sharing medium nodes of quadratic element whose medium nodes
9309   // are not all in mediumNodeIDs
9310
9311   // get remaining medium nodes
9312   TIDSortedNodeSet mediumNodes;
9313   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9314   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9315     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9316       mediumNodes.insert( mediumNodes.end(), n );
9317
9318   // find more quadratic elements to convert
9319   TIDSortedElemSet moreElemsToConvert;
9320   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9321   for ( ; nIt != mediumNodes.end(); ++nIt )
9322   {
9323     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9324     while ( invIt->more() )
9325     {
9326       const SMDS_MeshElement* e = invIt->next();
9327       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9328       {
9329         // find a more complex element including e and
9330         // whose medium nodes are not in mediumNodes
9331         bool complexFound = false;
9332         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9333         {
9334           SMDS_ElemIteratorPtr invIt2 =
9335             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9336           while ( invIt2->more() )
9337           {
9338             const SMDS_MeshElement* eComplex = invIt2->next();
9339             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9340             {
9341               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9342               if ( nbCommonNodes == e->NbNodes())
9343               {
9344                 complexFound = true;
9345                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9346                 break;
9347               }
9348             }
9349           }
9350         }
9351         if ( !complexFound )
9352           moreElemsToConvert.insert( e );
9353       }
9354     }
9355   }
9356   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9357   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9358 }
9359
9360 //=======================================================================
9361 //function : SewSideElements
9362 //purpose  :
9363 //=======================================================================
9364
9365 SMESH_MeshEditor::Sew_Error
9366 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9367                                    TIDSortedElemSet&    theSide2,
9368                                    const SMDS_MeshNode* theFirstNode1,
9369                                    const SMDS_MeshNode* theFirstNode2,
9370                                    const SMDS_MeshNode* theSecondNode1,
9371                                    const SMDS_MeshNode* theSecondNode2)
9372 {
9373   ClearLastCreated();
9374
9375   if ( theSide1.size() != theSide2.size() )
9376     return SEW_DIFF_NB_OF_ELEMENTS;
9377
9378   Sew_Error aResult = SEW_OK;
9379   // Algo:
9380   // 1. Build set of faces representing each side
9381   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9382   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9383
9384   // =======================================================================
9385   // 1. Build set of faces representing each side:
9386   // =======================================================================
9387   // a. build set of nodes belonging to faces
9388   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9389   // c. create temporary faces representing side of volumes if correspondent
9390   //    face does not exist
9391
9392   SMESHDS_Mesh* aMesh = GetMeshDS();
9393   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9394   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9395   TIDSortedElemSet             faceSet1, faceSet2;
9396   set<const SMDS_MeshElement*> volSet1,  volSet2;
9397   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9398   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9399   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9400   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9401   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9402   int iSide, iFace, iNode;
9403
9404   list<const SMDS_MeshElement* > tempFaceList;
9405   for ( iSide = 0; iSide < 2; iSide++ ) {
9406     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9407     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9408     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9409     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9410     set<const SMDS_MeshElement*>::iterator vIt;
9411     TIDSortedElemSet::iterator eIt;
9412     set<const SMDS_MeshNode*>::iterator    nIt;
9413
9414     // check that given nodes belong to given elements
9415     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9416     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9417     int firstIndex = -1, secondIndex = -1;
9418     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9419       const SMDS_MeshElement* elem = *eIt;
9420       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9421       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9422       if ( firstIndex > -1 && secondIndex > -1 ) break;
9423     }
9424     if ( firstIndex < 0 || secondIndex < 0 ) {
9425       // we can simply return until temporary faces created
9426       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9427     }
9428
9429     // -----------------------------------------------------------
9430     // 1a. Collect nodes of existing faces
9431     //     and build set of face nodes in order to detect missing
9432     //     faces corresponding to sides of volumes
9433     // -----------------------------------------------------------
9434
9435     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9436
9437     // loop on the given element of a side
9438     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9439       //const SMDS_MeshElement* elem = *eIt;
9440       const SMDS_MeshElement* elem = *eIt;
9441       if ( elem->GetType() == SMDSAbs_Face ) {
9442         faceSet->insert( elem );
9443         set <const SMDS_MeshNode*> faceNodeSet;
9444         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9445         while ( nodeIt->more() ) {
9446           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9447           nodeSet->insert( n );
9448           faceNodeSet.insert( n );
9449         }
9450         setOfFaceNodeSet.insert( faceNodeSet );
9451       }
9452       else if ( elem->GetType() == SMDSAbs_Volume )
9453         volSet->insert( elem );
9454     }
9455     // ------------------------------------------------------------------------------
9456     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9457     // ------------------------------------------------------------------------------
9458
9459     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9460       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9461       while ( fIt->more() ) { // loop on faces sharing a node
9462         const SMDS_MeshElement* f = fIt->next();
9463         if ( faceSet->find( f ) == faceSet->end() ) {
9464           // check if all nodes are in nodeSet and
9465           // complete setOfFaceNodeSet if they are
9466           set <const SMDS_MeshNode*> faceNodeSet;
9467           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9468           bool allInSet = true;
9469           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9470             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9471             if ( nodeSet->find( n ) == nodeSet->end() )
9472               allInSet = false;
9473             else
9474               faceNodeSet.insert( n );
9475           }
9476           if ( allInSet ) {
9477             faceSet->insert( f );
9478             setOfFaceNodeSet.insert( faceNodeSet );
9479           }
9480         }
9481       }
9482     }
9483
9484     // -------------------------------------------------------------------------
9485     // 1c. Create temporary faces representing sides of volumes if correspondent
9486     //     face does not exist
9487     // -------------------------------------------------------------------------
9488
9489     if ( !volSet->empty() ) {
9490       //int nodeSetSize = nodeSet->size();
9491
9492       // loop on given volumes
9493       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9494         SMDS_VolumeTool vol (*vIt);
9495         // loop on volume faces: find free faces
9496         // --------------------------------------
9497         list<const SMDS_MeshElement* > freeFaceList;
9498         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9499           if ( !vol.IsFreeFace( iFace ))
9500             continue;
9501           // check if there is already a face with same nodes in a face set
9502           const SMDS_MeshElement* aFreeFace = 0;
9503           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9504           int nbNodes = vol.NbFaceNodes( iFace );
9505           set <const SMDS_MeshNode*> faceNodeSet;
9506           vol.GetFaceNodes( iFace, faceNodeSet );
9507           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9508           if ( isNewFace ) {
9509             // no such a face is given but it still can exist, check it
9510             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9511             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9512           }
9513           if ( !aFreeFace ) {
9514             // create a temporary face
9515             if ( nbNodes == 3 ) {
9516               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9517               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9518             }
9519             else if ( nbNodes == 4 ) {
9520               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9521               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9522             }
9523             else {
9524               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9525               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9526               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9527             }
9528             if ( aFreeFace )
9529               tempFaceList.push_back( aFreeFace );
9530           }
9531
9532           if ( aFreeFace )
9533             freeFaceList.push_back( aFreeFace );
9534
9535         } // loop on faces of a volume
9536
9537         // choose one of several free faces of a volume
9538         // --------------------------------------------
9539         if ( freeFaceList.size() > 1 ) {
9540           // choose a face having max nb of nodes shared by other elems of a side
9541           int maxNbNodes = -1;
9542           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9543           while ( fIt != freeFaceList.end() ) { // loop on free faces
9544             int nbSharedNodes = 0;
9545             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9546             while ( nodeIt->more() ) { // loop on free face nodes
9547               const SMDS_MeshNode* n =
9548                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9549               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9550               while ( invElemIt->more() ) {
9551                 const SMDS_MeshElement* e = invElemIt->next();
9552                 nbSharedNodes += faceSet->count( e );
9553                 nbSharedNodes += elemSet->count( e );
9554               }
9555             }
9556             if ( nbSharedNodes > maxNbNodes ) {
9557               maxNbNodes = nbSharedNodes;
9558               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9559             }
9560             else if ( nbSharedNodes == maxNbNodes ) {
9561               fIt++;
9562             }
9563             else {
9564               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9565             }
9566           }
9567           if ( freeFaceList.size() > 1 )
9568           {
9569             // could not choose one face, use another way
9570             // choose a face most close to the bary center of the opposite side
9571             gp_XYZ aBC( 0., 0., 0. );
9572             set <const SMDS_MeshNode*> addedNodes;
9573             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9574             eIt = elemSet2->begin();
9575             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9576               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9577               while ( nodeIt->more() ) { // loop on free face nodes
9578                 const SMDS_MeshNode* n =
9579                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9580                 if ( addedNodes.insert( n ).second )
9581                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9582               }
9583             }
9584             aBC /= addedNodes.size();
9585             double minDist = DBL_MAX;
9586             fIt = freeFaceList.begin();
9587             while ( fIt != freeFaceList.end() ) { // loop on free faces
9588               double dist = 0;
9589               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9590               while ( nodeIt->more() ) { // loop on free face nodes
9591                 const SMDS_MeshNode* n =
9592                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9593                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9594                 dist += ( aBC - p ).SquareModulus();
9595               }
9596               if ( dist < minDist ) {
9597                 minDist = dist;
9598                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9599               }
9600               else
9601                 fIt = freeFaceList.erase( fIt++ );
9602             }
9603           }
9604         } // choose one of several free faces of a volume
9605
9606         if ( freeFaceList.size() == 1 ) {
9607           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9608           faceSet->insert( aFreeFace );
9609           // complete a node set with nodes of a found free face
9610           //           for ( iNode = 0; iNode < ; iNode++ )
9611           //             nodeSet->insert( fNodes[ iNode ] );
9612         }
9613
9614       } // loop on volumes of a side
9615
9616       //       // complete a set of faces if new nodes in a nodeSet appeared
9617       //       // ----------------------------------------------------------
9618       //       if ( nodeSetSize != nodeSet->size() ) {
9619       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9620       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9621       //           while ( fIt->more() ) { // loop on faces sharing a node
9622       //             const SMDS_MeshElement* f = fIt->next();
9623       //             if ( faceSet->find( f ) == faceSet->end() ) {
9624       //               // check if all nodes are in nodeSet and
9625       //               // complete setOfFaceNodeSet if they are
9626       //               set <const SMDS_MeshNode*> faceNodeSet;
9627       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9628       //               bool allInSet = true;
9629       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9630       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9631       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9632       //                   allInSet = false;
9633       //                 else
9634       //                   faceNodeSet.insert( n );
9635       //               }
9636       //               if ( allInSet ) {
9637       //                 faceSet->insert( f );
9638       //                 setOfFaceNodeSet.insert( faceNodeSet );
9639       //               }
9640       //             }
9641       //           }
9642       //         }
9643       //       }
9644     } // Create temporary faces, if there are volumes given
9645   } // loop on sides
9646
9647   if ( faceSet1.size() != faceSet2.size() ) {
9648     // delete temporary faces: they are in reverseElements of actual nodes
9649     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9650     //    while ( tmpFaceIt->more() )
9651     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9652     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9653     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9654     //      aMesh->RemoveElement(*tmpFaceIt);
9655     MESSAGE("Diff nb of faces");
9656     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9657   }
9658
9659   // ============================================================
9660   // 2. Find nodes to merge:
9661   //              bind a node to remove to a node to put instead
9662   // ============================================================
9663
9664   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9665   if ( theFirstNode1 != theFirstNode2 )
9666     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9667   if ( theSecondNode1 != theSecondNode2 )
9668     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9669
9670   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9671   set< long > linkIdSet; // links to process
9672   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9673
9674   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9675   list< NLink > linkList[2];
9676   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9677   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9678   // loop on links in linkList; find faces by links and append links
9679   // of the found faces to linkList
9680   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9681   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9682   {
9683     NLink link[] = { *linkIt[0], *linkIt[1] };
9684     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9685     if ( !linkIdSet.count( linkID ) )
9686       continue;
9687
9688     // by links, find faces in the face sets,
9689     // and find indices of link nodes in the found faces;
9690     // in a face set, there is only one or no face sharing a link
9691     // ---------------------------------------------------------------
9692
9693     const SMDS_MeshElement* face[] = { 0, 0 };
9694     vector<const SMDS_MeshNode*> fnodes[2];
9695     int iLinkNode[2][2];
9696     TIDSortedElemSet avoidSet;
9697     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9698       const SMDS_MeshNode* n1 = link[iSide].first;
9699       const SMDS_MeshNode* n2 = link[iSide].second;
9700       //cout << "Side " << iSide << " ";
9701       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9702       // find a face by two link nodes
9703       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9704                                                       *faceSetPtr[ iSide ], avoidSet,
9705                                                       &iLinkNode[iSide][0],
9706                                                       &iLinkNode[iSide][1] );
9707       if ( face[ iSide ])
9708       {
9709         //cout << " F " << face[ iSide]->GetID() <<endl;
9710         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9711         // put face nodes to fnodes
9712         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9713         fnodes[ iSide ].assign( nIt, nEnd );
9714         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9715       }
9716     }
9717
9718     // check similarity of elements of the sides
9719     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9720       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9721       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9722         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9723       }
9724       else {
9725         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9726       }
9727       break; // do not return because it's necessary to remove tmp faces
9728     }
9729
9730     // set nodes to merge
9731     // -------------------
9732
9733     if ( face[0] && face[1] )  {
9734       const int nbNodes = face[0]->NbNodes();
9735       if ( nbNodes != face[1]->NbNodes() ) {
9736         MESSAGE("Diff nb of face nodes");
9737         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9738         break; // do not return because it s necessary to remove tmp faces
9739       }
9740       bool reverse[] = { false, false }; // order of nodes in the link
9741       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9742         // analyse link orientation in faces
9743         int i1 = iLinkNode[ iSide ][ 0 ];
9744         int i2 = iLinkNode[ iSide ][ 1 ];
9745         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9746       }
9747       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9748       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9749       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9750       {
9751         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9752                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9753       }
9754
9755       // add other links of the faces to linkList
9756       // -----------------------------------------
9757
9758       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9759         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9760         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9761         if ( !iter_isnew.second ) { // already in a set: no need to process
9762           linkIdSet.erase( iter_isnew.first );
9763         }
9764         else // new in set == encountered for the first time: add
9765         {
9766           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9767           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9768           linkList[0].push_back ( NLink( n1, n2 ));
9769           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9770         }
9771       }
9772     } // 2 faces found
9773
9774     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9775       break;
9776
9777   } // loop on link lists
9778
9779   if ( aResult == SEW_OK &&
9780        ( //linkIt[0] != linkList[0].end() ||
9781         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9782     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9783              " " << (faceSetPtr[1]->empty()));
9784     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9785   }
9786
9787   // ====================================================================
9788   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9789   // ====================================================================
9790
9791   // delete temporary faces
9792   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9793   //  while ( tmpFaceIt->more() )
9794   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9795   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9796   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9797     aMesh->RemoveElement(*tmpFaceIt);
9798
9799   if ( aResult != SEW_OK)
9800     return aResult;
9801
9802   list< int > nodeIDsToRemove;
9803   vector< const SMDS_MeshNode*> nodes;
9804   ElemFeatures elemType;
9805
9806   // loop on nodes replacement map
9807   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9808   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9809     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9810     {
9811       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9812       nodeIDsToRemove.push_back( nToRemove->GetID() );
9813       // loop on elements sharing nToRemove
9814       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9815       while ( invElemIt->more() ) {
9816         const SMDS_MeshElement* e = invElemIt->next();
9817         // get a new suite of nodes: make replacement
9818         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9819         nodes.resize( nbNodes );
9820         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9821         while ( nIt->more() ) {
9822           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9823           nnIt = nReplaceMap.find( n );
9824           if ( nnIt != nReplaceMap.end() ) {
9825             nbReplaced++;
9826             n = (*nnIt).second;
9827           }
9828           nodes[ i++ ] = n;
9829         }
9830         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9831         //         elemIDsToRemove.push_back( e->GetID() );
9832         //       else
9833         if ( nbReplaced )
9834         {
9835           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9836           aMesh->RemoveElement( e );
9837
9838           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9839           {
9840             AddToSameGroups( newElem, e, aMesh );
9841             if ( int aShapeId = e->getshapeId() )
9842               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9843           }
9844         }
9845       }
9846     }
9847
9848   Remove( nodeIDsToRemove, true );
9849
9850   return aResult;
9851 }
9852
9853 //================================================================================
9854 /*!
9855  * \brief Find corresponding nodes in two sets of faces
9856  * \param theSide1 - first face set
9857  * \param theSide2 - second first face
9858  * \param theFirstNode1 - a boundary node of set 1
9859  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9860  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9861  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9862  * \param nReplaceMap - output map of corresponding nodes
9863  * \return bool  - is a success or not
9864  */
9865 //================================================================================
9866
9867 #ifdef _DEBUG_
9868 //#define DEBUG_MATCHING_NODES
9869 #endif
9870
9871 SMESH_MeshEditor::Sew_Error
9872 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9873                                     set<const SMDS_MeshElement*>& theSide2,
9874                                     const SMDS_MeshNode*          theFirstNode1,
9875                                     const SMDS_MeshNode*          theFirstNode2,
9876                                     const SMDS_MeshNode*          theSecondNode1,
9877                                     const SMDS_MeshNode*          theSecondNode2,
9878                                     TNodeNodeMap &                nReplaceMap)
9879 {
9880   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9881
9882   nReplaceMap.clear();
9883   //if ( theFirstNode1 != theFirstNode2 )
9884   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9885   //if ( theSecondNode1 != theSecondNode2 )
9886   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9887
9888   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9889   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9890
9891   list< NLink > linkList[2];
9892   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9893   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9894
9895   // loop on links in linkList; find faces by links and append links
9896   // of the found faces to linkList
9897   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9898   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9899     NLink link[] = { *linkIt[0], *linkIt[1] };
9900     if ( linkSet.find( link[0] ) == linkSet.end() )
9901       continue;
9902
9903     // by links, find faces in the face sets,
9904     // and find indices of link nodes in the found faces;
9905     // in a face set, there is only one or no face sharing a link
9906     // ---------------------------------------------------------------
9907
9908     const SMDS_MeshElement* face[] = { 0, 0 };
9909     list<const SMDS_MeshNode*> notLinkNodes[2];
9910     //bool reverse[] = { false, false }; // order of notLinkNodes
9911     int nbNodes[2];
9912     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9913     {
9914       const SMDS_MeshNode* n1 = link[iSide].first;
9915       const SMDS_MeshNode* n2 = link[iSide].second;
9916       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9917       set< const SMDS_MeshElement* > facesOfNode1;
9918       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9919       {
9920         // during a loop of the first node, we find all faces around n1,
9921         // during a loop of the second node, we find one face sharing both n1 and n2
9922         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9923         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9924         while ( fIt->more() ) { // loop on faces sharing a node
9925           const SMDS_MeshElement* f = fIt->next();
9926           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9927               ! facesOfNode1.insert( f ).second ) // f encounters twice
9928           {
9929             if ( face[ iSide ] ) {
9930               MESSAGE( "2 faces per link " );
9931               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9932             }
9933             face[ iSide ] = f;
9934             faceSet->erase( f );
9935
9936             // get not link nodes
9937             int nbN = f->NbNodes();
9938             if ( f->IsQuadratic() )
9939               nbN /= 2;
9940             nbNodes[ iSide ] = nbN;
9941             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9942             int i1 = f->GetNodeIndex( n1 );
9943             int i2 = f->GetNodeIndex( n2 );
9944             int iEnd = nbN, iBeg = -1, iDelta = 1;
9945             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9946             if ( reverse ) {
9947               std::swap( iEnd, iBeg ); iDelta = -1;
9948             }
9949             int i = i2;
9950             while ( true ) {
9951               i += iDelta;
9952               if ( i == iEnd ) i = iBeg + iDelta;
9953               if ( i == i1 ) break;
9954               nodes.push_back ( f->GetNode( i ) );
9955             }
9956           }
9957         }
9958       }
9959     }
9960     // check similarity of elements of the sides
9961     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9962       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9963       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9964         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9965       }
9966       else {
9967         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9968       }
9969     }
9970
9971     // set nodes to merge
9972     // -------------------
9973
9974     if ( face[0] && face[1] )  {
9975       if ( nbNodes[0] != nbNodes[1] ) {
9976         MESSAGE("Diff nb of face nodes");
9977         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9978       }
9979 #ifdef DEBUG_MATCHING_NODES
9980       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9981                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9982                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9983 #endif
9984       int nbN = nbNodes[0];
9985       {
9986         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9987         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9988         for ( int i = 0 ; i < nbN - 2; ++i ) {
9989 #ifdef DEBUG_MATCHING_NODES
9990           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9991 #endif
9992           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9993         }
9994       }
9995
9996       // add other links of the face 1 to linkList
9997       // -----------------------------------------
9998
9999       const SMDS_MeshElement* f0 = face[0];
10000       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10001       for ( int i = 0; i < nbN; i++ )
10002       {
10003         const SMDS_MeshNode* n2 = f0->GetNode( i );
10004         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10005           linkSet.insert( SMESH_TLink( n1, n2 ));
10006         if ( !iter_isnew.second ) { // already in a set: no need to process
10007           linkSet.erase( iter_isnew.first );
10008         }
10009         else // new in set == encountered for the first time: add
10010         {
10011 #ifdef DEBUG_MATCHING_NODES
10012           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10013                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10014 #endif
10015           linkList[0].push_back ( NLink( n1, n2 ));
10016           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10017         }
10018         n1 = n2;
10019       }
10020     } // 2 faces found
10021   } // loop on link lists
10022
10023   return SEW_OK;
10024 }
10025
10026 namespace // automatically find theAffectedElems for DoubleNodes()
10027 {
10028   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10029
10030   //--------------------------------------------------------------------------------
10031   // Nodes shared by adjacent FissureBorder's.
10032   // 1 node  if FissureBorder separates faces
10033   // 2 nodes if FissureBorder separates volumes
10034   struct SubBorder
10035   {
10036     const SMDS_MeshNode* _nodes[2];
10037     int                  _nbNodes;
10038
10039     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10040     {
10041       _nodes[0] = n1;
10042       _nodes[1] = n2;
10043       _nbNodes = bool( n1 ) + bool( n2 );
10044       if ( _nbNodes == 2 && n1 > n2 )
10045         std::swap( _nodes[0], _nodes[1] );
10046     }
10047     bool operator<( const SubBorder& other ) const
10048     {
10049       for ( int i = 0; i < _nbNodes; ++i )
10050       {
10051         if ( _nodes[i] < other._nodes[i] ) return true;
10052         if ( _nodes[i] > other._nodes[i] ) return false;
10053       }
10054       return false;
10055     }
10056   };
10057
10058   //--------------------------------------------------------------------------------
10059   // Map a SubBorder to all FissureBorder it bounds
10060   struct FissureBorder;
10061   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10062   typedef TBorderLinks::iterator                               TMappedSub;
10063
10064   //--------------------------------------------------------------------------------
10065   /*!
10066    * \brief Element border (volume facet or face edge) at a fissure
10067    */
10068   struct FissureBorder
10069   {
10070     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10071     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10072
10073     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10074     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10075
10076     FissureBorder( FissureBorder && from ) // move constructor
10077     {
10078       std::swap( _nodes,       from._nodes );
10079       std::swap( _sortedNodes, from._sortedNodes );
10080       _elems[0] = from._elems[0];
10081       _elems[1] = from._elems[1];
10082     }
10083
10084     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10085                    std::vector< const SMDS_MeshElement* > & adjElems)
10086       : _nodes( elemToDuplicate->NbCornerNodes() )
10087     {
10088       for ( size_t i = 0; i < _nodes.size(); ++i )
10089         _nodes[i] = elemToDuplicate->GetNode( i );
10090
10091       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10092       findAdjacent( type, adjElems );
10093     }
10094
10095     FissureBorder( const SMDS_MeshNode**                    nodes,
10096                    const size_t                             nbNodes,
10097                    const SMDSAbs_ElementType                adjElemsType,
10098                    std::vector< const SMDS_MeshElement* > & adjElems)
10099       : _nodes( nodes, nodes + nbNodes )
10100     {
10101       findAdjacent( adjElemsType, adjElems );
10102     }
10103
10104     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10105                        std::vector< const SMDS_MeshElement* > & adjElems)
10106     {
10107       _elems[0] = _elems[1] = 0;
10108       adjElems.clear();
10109       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10110         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10111           _elems[i] = adjElems[i];
10112     }
10113
10114     bool operator<( const FissureBorder& other ) const
10115     {
10116       return GetSortedNodes() < other.GetSortedNodes();
10117     }
10118
10119     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10120     {
10121       if ( _sortedNodes.empty() && !_nodes.empty() )
10122       {
10123         FissureBorder* me = const_cast<FissureBorder*>( this );
10124         me->_sortedNodes = me->_nodes;
10125         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10126       }
10127       return _sortedNodes;
10128     }
10129
10130     size_t NbSub() const
10131     {
10132       return _nodes.size();
10133     }
10134
10135     SubBorder Sub(size_t i) const
10136     {
10137       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10138     }
10139
10140     void AddSelfTo( TBorderLinks& borderLinks )
10141     {
10142       _mappedSubs.resize( NbSub() );
10143       for ( size_t i = 0; i < NbSub(); ++i )
10144       {
10145         TBorderLinks::iterator s2b =
10146           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10147         s2b->second.push_back( this );
10148         _mappedSubs[ i ] = s2b;
10149       }
10150     }
10151
10152     void Clear()
10153     {
10154       _nodes.clear();
10155     }
10156
10157     const SMDS_MeshElement* GetMarkedElem() const
10158     {
10159       if ( _nodes.empty() ) return 0; // cleared
10160       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10161       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10162       return 0;
10163     }
10164
10165     gp_XYZ GetNorm() const // normal to the border
10166     {
10167       gp_XYZ norm;
10168       if ( _nodes.size() == 2 )
10169       {
10170         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10171         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10172           avgNorm += norm;
10173         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10174           avgNorm += norm;
10175
10176         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10177         norm = bordDir ^ avgNorm;
10178       }
10179       else
10180       {
10181         SMESH_NodeXYZ p0( _nodes[0] );
10182         SMESH_NodeXYZ p1( _nodes[1] );
10183         SMESH_NodeXYZ p2( _nodes[2] );
10184         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10185       }
10186       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10187         norm.Reverse();
10188
10189       return norm;
10190     }
10191
10192     void ChooseSide() // mark an _elem located at positive side of fissure
10193     {
10194       _elems[0]->setIsMarked( true );
10195       gp_XYZ norm = GetNorm();
10196       double maxX = norm.Coord(1);
10197       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10198       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10199       if ( maxX < 0 )
10200       {
10201         _elems[0]->setIsMarked( false );
10202         _elems[1]->setIsMarked( true );
10203       }
10204     }
10205
10206   }; // struct FissureBorder
10207
10208   //--------------------------------------------------------------------------------
10209   /*!
10210    * \brief Classifier of elements at fissure edge
10211    */
10212   class FissureNormal
10213   {
10214     std::vector< gp_XYZ > _normals;
10215     bool                  _bothIn;
10216
10217   public:
10218     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10219     {
10220       _bothIn = false;
10221       _normals.reserve(2);
10222       _normals.push_back( bord.GetNorm() );
10223       if ( _normals.size() == 2 )
10224         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10225     }
10226
10227     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10228     {
10229       bool isIn = false;
10230       switch ( _normals.size() ) {
10231       case 1:
10232       {
10233         isIn = !isOut( n, _normals[0], elem );
10234         break;
10235       }
10236       case 2:
10237       {
10238         bool in1 = !isOut( n, _normals[0], elem );
10239         bool in2 = !isOut( n, _normals[1], elem );
10240         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10241       }
10242       }
10243       return isIn;
10244     }
10245   };
10246
10247   //================================================================================
10248   /*!
10249    * \brief Classify an element by a plane passing through a node
10250    */
10251   //================================================================================
10252
10253   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10254   {
10255     SMESH_NodeXYZ p = n;
10256     double sumDot = 0;
10257     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10258     {
10259       SMESH_NodeXYZ pi = elem->GetNode( i );
10260       sumDot += norm * ( pi - p );
10261     }
10262     return sumDot < -1e-100;
10263   }
10264
10265   //================================================================================
10266   /*!
10267    * \brief Find FissureBorder's by nodes to duplicate
10268    */
10269   //================================================================================
10270
10271   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10272                            std::vector< FissureBorder > & theFissureBorders )
10273   {
10274     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10275     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10276     if ( !n ) return;
10277     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10278     if ( n->NbInverseElements( elemType ) == 0 )
10279     {
10280       elemType = SMDSAbs_Face;
10281       if ( n->NbInverseElements( elemType ) == 0 )
10282         return;
10283     }
10284     // unmark elements touching the fissure
10285     for ( ; nIt != theNodes.end(); ++nIt )
10286       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10287
10288     // loop on elements touching the fissure to get their borders belonging to the fissure
10289     std::set< FissureBorder >              fissureBorders;
10290     std::vector< const SMDS_MeshElement* > adjElems;
10291     std::vector< const SMDS_MeshNode* >    nodes;
10292     SMDS_VolumeTool volTool;
10293     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10294     {
10295       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10296       while ( invIt->more() )
10297       {
10298         const SMDS_MeshElement* eInv = invIt->next();
10299         if ( eInv->isMarked() ) continue;
10300         eInv->setIsMarked( true );
10301
10302         if ( elemType == SMDSAbs_Volume )
10303         {
10304           volTool.Set( eInv );
10305           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10306           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10307           {
10308             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10309             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10310             nodes.clear();
10311             bool allOnFissure = true;
10312             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10313               if (( allOnFissure = theNodes.count( nn[ iN ])))
10314                 nodes.push_back( nn[ iN ]);
10315             if ( allOnFissure )
10316               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10317                                                                elemType, adjElems )));
10318           }
10319         }
10320         else // elemType == SMDSAbs_Face
10321         {
10322           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10323           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10324           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10325           {
10326             nn[1]      = eInv->GetNode( iN );
10327             onFissure1 = theNodes.count( nn[1] );
10328             if ( onFissure0 && onFissure1 )
10329               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10330             nn[0]      = nn[1];
10331             onFissure0 = onFissure1;
10332           }
10333         }
10334       }
10335     }
10336
10337     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10338     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10339     for ( ; bord != fissureBorders.end(); ++bord )
10340     {
10341       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10342     }
10343     return;
10344   } // findFissureBorders()
10345
10346   //================================================================================
10347   /*!
10348    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10349    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10350    *  \param [in] theNodesNot - nodes not to duplicate
10351    *  \param [out] theAffectedElems - the found elements
10352    */
10353   //================================================================================
10354
10355   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10356                           TIDSortedElemSet&       theAffectedElems)
10357   {
10358     if ( theElemsOrNodes.empty() ) return;
10359
10360     // find FissureBorder's
10361
10362     std::vector< FissureBorder >           fissure;
10363     std::vector< const SMDS_MeshElement* > elemsByFacet;
10364
10365     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10366     if ( (*elIt)->GetType() == SMDSAbs_Node )
10367     {
10368       findFissureBorders( theElemsOrNodes, fissure );
10369     }
10370     else
10371     {
10372       fissure.reserve( theElemsOrNodes.size() );
10373       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10374         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10375     }
10376     if ( fissure.empty() )
10377       return;
10378
10379     // fill borderLinks
10380
10381     TBorderLinks borderLinks;
10382
10383     for ( size_t i = 0; i < fissure.size(); ++i )
10384     {
10385       fissure[i].AddSelfTo( borderLinks );
10386     }
10387
10388     // get theAffectedElems
10389
10390     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10391     for ( size_t i = 0; i < fissure.size(); ++i )
10392       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10393       {
10394         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10395                                         false, /*markElem=*/true );
10396       }
10397
10398     std::vector<const SMDS_MeshNode *>                 facetNodes;
10399     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10400     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10401
10402     // choose a side of fissure
10403     fissure[0].ChooseSide();
10404     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10405
10406     size_t nbCheckedBorders = 0;
10407     while ( nbCheckedBorders < fissure.size() )
10408     {
10409       // find a FissureBorder to treat
10410       FissureBorder* bord = 0;
10411       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10412         if ( fissure[i].GetMarkedElem() )
10413           bord = & fissure[i];
10414       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10415         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10416         {
10417           bord = & fissure[i];
10418           bord->ChooseSide();
10419           theAffectedElems.insert( bord->GetMarkedElem() );
10420         }
10421       if ( !bord ) return;
10422       ++nbCheckedBorders;
10423
10424       // treat FissureBorder's linked to bord
10425       fissureNodes.clear();
10426       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10427       for ( size_t i = 0; i < bord->NbSub(); ++i )
10428       {
10429         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10430         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10431         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10432         const SubBorder&                          sb = l2b->first;
10433         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10434
10435         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10436         {
10437           for ( int j = 0; j < sb._nbNodes; ++j )
10438             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10439           continue;
10440         }
10441
10442         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10443         // until an elem adjacent to a neighbour FissureBorder is found
10444         facetNodes.clear();
10445         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10446         facetNodes.resize( sb._nbNodes + 1 );
10447
10448         while ( bordElem )
10449         {
10450           // check if bordElem is adjacent to a neighbour FissureBorder
10451           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10452           {
10453             FissureBorder* bord2 = linkedBorders[j];
10454             if ( bord2 == bord ) continue;
10455             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10456               bordElem = 0;
10457             else
10458               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10459           }
10460           if ( !bordElem )
10461             break;
10462
10463           // find the next bordElem
10464           const SMDS_MeshElement* nextBordElem = 0;
10465           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10466           {
10467             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10468             if ( fissureNodes.count( n )) continue;
10469
10470             facetNodes[ sb._nbNodes ] = n;
10471             elemsByFacet.clear();
10472             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10473             {
10474               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10475                 if ( elemsByFacet[ iE ] != bordElem &&
10476                      !elemsByFacet[ iE ]->isMarked() )
10477                 {
10478                   theAffectedElems.insert( elemsByFacet[ iE ]);
10479                   elemsByFacet[ iE ]->setIsMarked( true );
10480                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10481                     nextBordElem = elemsByFacet[ iE ];
10482                 }
10483             }
10484           }
10485           bordElem = nextBordElem;
10486
10487         } // while ( bordElem )
10488
10489         linkedBorders.clear(); // not to treat this link any more
10490
10491       } // loop on SubBorder's of a FissureBorder
10492
10493       bord->Clear();
10494
10495     } // loop on FissureBorder's
10496
10497
10498     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10499
10500     // mark nodes of theAffectedElems
10501     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10502
10503     // unmark nodes of the fissure
10504     elIt = theElemsOrNodes.begin();
10505     if ( (*elIt)->GetType() == SMDSAbs_Node )
10506       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10507     else
10508       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10509
10510     std::vector< gp_XYZ > normVec;
10511
10512     // loop on nodes of the fissure, add elements having marked nodes
10513     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10514     {
10515       const SMDS_MeshElement* e = (*elIt);
10516       if ( e->GetType() != SMDSAbs_Node )
10517         e->setIsMarked( true ); // avoid adding a fissure element
10518
10519       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10520       {
10521         const SMDS_MeshNode* n = e->GetNode( iN );
10522         if ( fissEdgeNodes2Norm.count( n ))
10523           continue;
10524
10525         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10526         while ( invIt->more() )
10527         {
10528           const SMDS_MeshElement* eInv = invIt->next();
10529           if ( eInv->isMarked() ) continue;
10530           eInv->setIsMarked( true );
10531
10532           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10533           while( nIt->more() )
10534             if ( nIt->next()->isMarked())
10535             {
10536               theAffectedElems.insert( eInv );
10537               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10538               n->setIsMarked( false );
10539               break;
10540             }
10541         }
10542       }
10543     }
10544
10545     // add elements on the fissure edge
10546     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10547     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10548     {
10549       const SMDS_MeshNode* edgeNode = n2N->first;
10550       const FissureNormal & normals = n2N->second;
10551
10552       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10553       while ( invIt->more() )
10554       {
10555         const SMDS_MeshElement* eInv = invIt->next();
10556         if ( eInv->isMarked() ) continue;
10557         eInv->setIsMarked( true );
10558
10559         // classify eInv using normals
10560         bool toAdd = normals.IsIn( edgeNode, eInv );
10561         if ( toAdd ) // check if all nodes lie on the fissure edge
10562         {
10563           bool notOnEdge = false;
10564           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10565             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10566           toAdd = notOnEdge;
10567         }
10568         if ( toAdd )
10569         {
10570           theAffectedElems.insert( eInv );
10571         }
10572       }
10573     }
10574
10575     return;
10576   } // findAffectedElems()
10577 } // namespace
10578
10579 //================================================================================
10580 /*!
10581  * \brief Create elements equal (on same nodes) to given ones
10582  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10583  *              elements of the uppest dimension are duplicated.
10584  */
10585 //================================================================================
10586
10587 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10588 {
10589   ClearLastCreated();
10590   SMESHDS_Mesh* mesh = GetMeshDS();
10591
10592   // get an element type and an iterator over elements
10593
10594   SMDSAbs_ElementType type = SMDSAbs_All;
10595   SMDS_ElemIteratorPtr elemIt;
10596   if ( theElements.empty() )
10597   {
10598     if ( mesh->NbNodes() == 0 )
10599       return;
10600     // get most complex type
10601     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10602       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10603       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10604     };
10605     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10606       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10607       {
10608         type = types[i];
10609         elemIt = mesh->elementsIterator( type );
10610         break;
10611       }
10612   }
10613   else
10614   {
10615     //type = (*theElements.begin())->GetType();
10616     elemIt = SMESHUtils::elemSetIterator( theElements );
10617   }
10618
10619   // un-mark all elements to avoid duplicating just created elements
10620   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10621
10622   // duplicate elements
10623
10624   ElemFeatures elemType;
10625
10626   vector< const SMDS_MeshNode* > nodes;
10627   while ( elemIt->more() )
10628   {
10629     const SMDS_MeshElement* elem = elemIt->next();
10630     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10631         ( elem->isMarked() ))
10632       continue;
10633
10634     elemType.Init( elem, /*basicOnly=*/false );
10635     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10636
10637     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10638       newElem->setIsMarked( true );
10639   }
10640 }
10641
10642 //================================================================================
10643 /*!
10644   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10645   \param theElems - the list of elements (edges or faces) to be replicated
10646   The nodes for duplication could be found from these elements
10647   \param theNodesNot - list of nodes to NOT replicate
10648   \param theAffectedElems - the list of elements (cells and edges) to which the
10649   replicated nodes should be associated to.
10650   \return TRUE if operation has been completed successfully, FALSE otherwise
10651 */
10652 //================================================================================
10653
10654 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10655                                     const TIDSortedElemSet& theNodesNot,
10656                                     const TIDSortedElemSet& theAffectedElems )
10657 {
10658   ClearLastCreated();
10659
10660   if ( theElems.size() == 0 )
10661     return false;
10662
10663   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10664   if ( !aMeshDS )
10665     return false;
10666
10667   bool res = false;
10668   TNodeNodeMap anOldNodeToNewNode;
10669   // duplicate elements and nodes
10670   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10671   // replce nodes by duplications
10672   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10673   return res;
10674 }
10675
10676 //================================================================================
10677 /*!
10678   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10679   \param theMeshDS - mesh instance
10680   \param theElems - the elements replicated or modified (nodes should be changed)
10681   \param theNodesNot - nodes to NOT replicate
10682   \param theNodeNodeMap - relation of old node to new created node
10683   \param theIsDoubleElem - flag os to replicate element or modify
10684   \return TRUE if operation has been completed successfully, FALSE otherwise
10685 */
10686 //================================================================================
10687
10688 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10689                                    const TIDSortedElemSet& theElems,
10690                                    const TIDSortedElemSet& theNodesNot,
10691                                    TNodeNodeMap&           theNodeNodeMap,
10692                                    const bool              theIsDoubleElem )
10693 {
10694   // iterate through element and duplicate them (by nodes duplication)
10695   bool res = false;
10696   std::vector<const SMDS_MeshNode*> newNodes;
10697   ElemFeatures elemType;
10698
10699   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10700   for ( ;  elemItr != theElems.end(); ++elemItr )
10701   {
10702     const SMDS_MeshElement* anElem = *elemItr;
10703     // if (!anElem)
10704     //   continue;
10705
10706     // duplicate nodes to duplicate element
10707     bool isDuplicate = false;
10708     newNodes.resize( anElem->NbNodes() );
10709     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10710     int ind = 0;
10711     while ( anIter->more() )
10712     {
10713       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10714       const SMDS_MeshNode*  aNewNode = aCurrNode;
10715       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10716       if ( n2n != theNodeNodeMap.end() )
10717       {
10718         aNewNode = n2n->second;
10719       }
10720       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10721       {
10722         // duplicate node
10723         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10724         copyPosition( aCurrNode, aNewNode );
10725         theNodeNodeMap[ aCurrNode ] = aNewNode;
10726         myLastCreatedNodes.push_back( aNewNode );
10727       }
10728       isDuplicate |= (aCurrNode != aNewNode);
10729       newNodes[ ind++ ] = aNewNode;
10730     }
10731     if ( !isDuplicate )
10732       continue;
10733
10734     if ( theIsDoubleElem )
10735       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10736     else
10737       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10738
10739     res = true;
10740   }
10741   return res;
10742 }
10743
10744 //================================================================================
10745 /*!
10746   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10747   \param theNodes - identifiers of nodes to be doubled
10748   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10749   nodes. If list of element identifiers is empty then nodes are doubled but
10750   they not assigned to elements
10751   \return TRUE if operation has been completed successfully, FALSE otherwise
10752 */
10753 //================================================================================
10754
10755 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10756                                     const std::list< int >& theListOfModifiedElems )
10757 {
10758   ClearLastCreated();
10759
10760   if ( theListOfNodes.size() == 0 )
10761     return false;
10762
10763   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10764   if ( !aMeshDS )
10765     return false;
10766
10767   // iterate through nodes and duplicate them
10768
10769   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10770
10771   std::list< int >::const_iterator aNodeIter;
10772   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10773   {
10774     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10775     if ( !aNode )
10776       continue;
10777
10778     // duplicate node
10779
10780     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10781     if ( aNewNode )
10782     {
10783       copyPosition( aNode, aNewNode );
10784       anOldNodeToNewNode[ aNode ] = aNewNode;
10785       myLastCreatedNodes.push_back( aNewNode );
10786     }
10787   }
10788
10789   // Change nodes of elements
10790
10791   std::vector<const SMDS_MeshNode*> aNodeArr;
10792
10793   std::list< int >::const_iterator anElemIter;
10794   for ( anElemIter =  theListOfModifiedElems.begin();
10795         anElemIter != theListOfModifiedElems.end();
10796         anElemIter++ )
10797   {
10798     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10799     if ( !anElem )
10800       continue;
10801
10802     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10803     for( size_t i = 0; i < aNodeArr.size(); ++i )
10804     {
10805       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10806         anOldNodeToNewNode.find( aNodeArr[ i ]);
10807       if ( n2n != anOldNodeToNewNode.end() )
10808         aNodeArr[ i ] = n2n->second;
10809     }
10810     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10811   }
10812
10813   return true;
10814 }
10815
10816 namespace {
10817
10818   //================================================================================
10819   /*!
10820     \brief Check if element located inside shape
10821     \return TRUE if IN or ON shape, FALSE otherwise
10822   */
10823   //================================================================================
10824
10825   template<class Classifier>
10826   bool isInside(const SMDS_MeshElement* theElem,
10827                 Classifier&             theClassifier,
10828                 const double            theTol)
10829   {
10830     gp_XYZ centerXYZ (0, 0, 0);
10831     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10832       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10833
10834     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10835     theClassifier.Perform(aPnt, theTol);
10836     TopAbs_State aState = theClassifier.State();
10837     return (aState == TopAbs_IN || aState == TopAbs_ON );
10838   }
10839
10840   //================================================================================
10841   /*!
10842    * \brief Classifier of the 3D point on the TopoDS_Face
10843    *        with interaface suitable for isInside()
10844    */
10845   //================================================================================
10846
10847   struct _FaceClassifier
10848   {
10849     Extrema_ExtPS       _extremum;
10850     BRepAdaptor_Surface _surface;
10851     TopAbs_State        _state;
10852
10853     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10854     {
10855       _extremum.Initialize( _surface,
10856                             _surface.FirstUParameter(), _surface.LastUParameter(),
10857                             _surface.FirstVParameter(), _surface.LastVParameter(),
10858                             _surface.Tolerance(), _surface.Tolerance() );
10859     }
10860     void Perform(const gp_Pnt& aPnt, double theTol)
10861     {
10862       theTol *= theTol;
10863       _state = TopAbs_OUT;
10864       _extremum.Perform(aPnt);
10865       if ( _extremum.IsDone() )
10866         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10867           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10868     }
10869     TopAbs_State State() const
10870     {
10871       return _state;
10872     }
10873   };
10874 }
10875
10876 //================================================================================
10877 /*!
10878   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10879   This method is the first step of DoubleNodeElemGroupsInRegion.
10880   \param theElems - list of groups of elements (edges or faces) to be replicated
10881   \param theNodesNot - list of groups of nodes not to replicated
10882   \param theShape - shape to detect affected elements (element which geometric center
10883          located on or inside shape). If the shape is null, detection is done on faces orientations
10884          (select elements with a gravity center on the side given by faces normals).
10885          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10886          The replicated nodes should be associated to affected elements.
10887   \return true
10888   \sa DoubleNodeElemGroupsInRegion()
10889 */
10890 //================================================================================
10891
10892 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10893                                                    const TIDSortedElemSet& theNodesNot,
10894                                                    const TopoDS_Shape&     theShape,
10895                                                    TIDSortedElemSet&       theAffectedElems)
10896 {
10897   if ( theShape.IsNull() )
10898   {
10899     findAffectedElems( theElems, theAffectedElems );
10900   }
10901   else
10902   {
10903     const double aTol = Precision::Confusion();
10904     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10905     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10906     if ( theShape.ShapeType() == TopAbs_SOLID )
10907     {
10908       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10909       bsc3d->PerformInfinitePoint(aTol);
10910     }
10911     else if (theShape.ShapeType() == TopAbs_FACE )
10912     {
10913       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10914     }
10915
10916     // iterates on indicated elements and get elements by back references from their nodes
10917     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10918     for ( ;  elemItr != theElems.end(); ++elemItr )
10919     {
10920       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10921       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10922       while ( nodeItr->more() )
10923       {
10924         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10925         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10926           continue;
10927         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10928         while ( backElemItr->more() )
10929         {
10930           const SMDS_MeshElement* curElem = backElemItr->next();
10931           if ( curElem && theElems.find(curElem) == theElems.end() &&
10932                ( bsc3d.get() ?
10933                  isInside( curElem, *bsc3d, aTol ) :
10934                  isInside( curElem, *aFaceClassifier, aTol )))
10935             theAffectedElems.insert( curElem );
10936         }
10937       }
10938     }
10939   }
10940   return true;
10941 }
10942
10943 //================================================================================
10944 /*!
10945   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10946   \param theElems - group of of elements (edges or faces) to be replicated
10947   \param theNodesNot - group of nodes not to replicate
10948   \param theShape - shape to detect affected elements (element which geometric center
10949   located on or inside shape).
10950   The replicated nodes should be associated to affected elements.
10951   \return TRUE if operation has been completed successfully, FALSE otherwise
10952 */
10953 //================================================================================
10954
10955 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10956                                             const TIDSortedElemSet& theNodesNot,
10957                                             const TopoDS_Shape&     theShape )
10958 {
10959   if ( theShape.IsNull() )
10960     return false;
10961
10962   const double aTol = Precision::Confusion();
10963   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10964   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10965   if ( theShape.ShapeType() == TopAbs_SOLID )
10966   {
10967     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10968     bsc3d->PerformInfinitePoint(aTol);
10969   }
10970   else if (theShape.ShapeType() == TopAbs_FACE )
10971   {
10972     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10973   }
10974
10975   // iterates on indicated elements and get elements by back references from their nodes
10976   TIDSortedElemSet anAffected;
10977   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10978   for ( ;  elemItr != theElems.end(); ++elemItr )
10979   {
10980     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10981     if (!anElem)
10982       continue;
10983
10984     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10985     while ( nodeItr->more() )
10986     {
10987       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10988       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10989         continue;
10990       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10991       while ( backElemItr->more() )
10992       {
10993         const SMDS_MeshElement* curElem = backElemItr->next();
10994         if ( curElem && theElems.find(curElem) == theElems.end() &&
10995              ( bsc3d ?
10996                isInside( curElem, *bsc3d, aTol ) :
10997                isInside( curElem, *aFaceClassifier, aTol )))
10998           anAffected.insert( curElem );
10999       }
11000     }
11001   }
11002   return DoubleNodes( theElems, theNodesNot, anAffected );
11003 }
11004
11005 /*!
11006  *  \brief compute an oriented angle between two planes defined by four points.
11007  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11008  *  @param p0 base of the rotation axe
11009  *  @param p1 extremity of the rotation axe
11010  *  @param g1 belongs to the first plane
11011  *  @param g2 belongs to the second plane
11012  */
11013 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11014 {
11015   gp_Vec vref(p0, p1);
11016   gp_Vec v1(p0, g1);
11017   gp_Vec v2(p0, g2);
11018   gp_Vec n1 = vref.Crossed(v1);
11019   gp_Vec n2 = vref.Crossed(v2);
11020   try {
11021     return n2.AngleWithRef(n1, vref);
11022   }
11023   catch ( Standard_Failure ) {
11024   }
11025   return Max( v1.Magnitude(), v2.Magnitude() );
11026 }
11027
11028 /*!
11029  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11030  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11031  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11032  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11033  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11034  * 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.
11035  * 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.
11036  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11037  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11038  * \param theElems - list of groups of volumes, where a group of volume is a set of
11039  *        SMDS_MeshElements sorted by Id.
11040  * \param createJointElems - if TRUE, create the elements
11041  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11042  *        the boundary between \a theDomains and the rest mesh
11043  * \return TRUE if operation has been completed successfully, FALSE otherwise
11044  */
11045 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11046                                                      bool                                 createJointElems,
11047                                                      bool                                 onAllBoundaries)
11048 {
11049   // MESSAGE("----------------------------------------------");
11050   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11051   // MESSAGE("----------------------------------------------");
11052
11053   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11054   meshDS->BuildDownWardConnectivity(true);
11055   CHRONO(50);
11056   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11057
11058   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11059   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11060   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11061
11062   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11063   std::map<int,int>celldom; // cell vtkId --> domain
11064   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11065   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11066   faceDomains.clear();
11067   celldom.clear();
11068   cellDomains.clear();
11069   nodeDomains.clear();
11070   std::map<int,int> emptyMap;
11071   std::set<int> emptySet;
11072   emptyMap.clear();
11073
11074   //MESSAGE(".. Number of domains :"<<theElems.size());
11075
11076   TIDSortedElemSet theRestDomElems;
11077   const int iRestDom  = -1;
11078   const int idom0     = onAllBoundaries ? iRestDom : 0;
11079   const int nbDomains = theElems.size();
11080
11081   // Check if the domains do not share an element
11082   for (int idom = 0; idom < nbDomains-1; idom++)
11083   {
11084     //       MESSAGE("... Check of domain #" << idom);
11085     const TIDSortedElemSet& domain = theElems[idom];
11086     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11087     for (; elemItr != domain.end(); ++elemItr)
11088     {
11089       const SMDS_MeshElement* anElem = *elemItr;
11090       int idombisdeb = idom + 1 ;
11091       // check if the element belongs to a domain further in the list
11092       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11093       {
11094         const TIDSortedElemSet& domainbis = theElems[idombis];
11095         if ( domainbis.count( anElem ))
11096         {
11097           MESSAGE(".... Domain #" << idom);
11098           MESSAGE(".... Domain #" << idombis);
11099           throw SALOME_Exception("The domains are not disjoint.");
11100           return false ;
11101         }
11102       }
11103     }
11104   }
11105
11106   for (int idom = 0; idom < nbDomains; idom++)
11107   {
11108
11109     // --- build a map (face to duplicate --> volume to modify)
11110     //     with all the faces shared by 2 domains (group of elements)
11111     //     and corresponding volume of this domain, for each shared face.
11112     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11113
11114     //MESSAGE("... Neighbors of domain #" << idom);
11115     const TIDSortedElemSet& domain = theElems[idom];
11116     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11117     for (; elemItr != domain.end(); ++elemItr)
11118     {
11119       const SMDS_MeshElement* anElem = *elemItr;
11120       if (!anElem)
11121         continue;
11122       int vtkId = anElem->GetVtkID();
11123       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11124       int neighborsVtkIds[NBMAXNEIGHBORS];
11125       int downIds[NBMAXNEIGHBORS];
11126       unsigned char downTypes[NBMAXNEIGHBORS];
11127       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11128       for (int n = 0; n < nbNeighbors; n++)
11129       {
11130         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11131         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11132         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11133         {
11134           bool ok = false;
11135           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11136           {
11137             // MESSAGE("Domain " << idombis);
11138             const TIDSortedElemSet& domainbis = theElems[idombis];
11139             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11140           }
11141           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11142           {
11143             DownIdType face(downIds[n], downTypes[n]);
11144             if (!faceDomains[face].count(idom))
11145             {
11146               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11147               celldom[vtkId] = idom;
11148               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11149             }
11150             if ( !ok )
11151             {
11152               theRestDomElems.insert( elem );
11153               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11154               celldom[neighborsVtkIds[n]] = iRestDom;
11155             }
11156           }
11157         }
11158       }
11159     }
11160   }
11161
11162   //MESSAGE("Number of shared faces " << faceDomains.size());
11163   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11164
11165   // --- explore the shared faces domain by domain,
11166   //     explore the nodes of the face and see if they belong to a cell in the domain,
11167   //     which has only a node or an edge on the border (not a shared face)
11168
11169   for (int idomain = idom0; idomain < nbDomains; idomain++)
11170   {
11171     //MESSAGE("Domain " << idomain);
11172     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11173     itface = faceDomains.begin();
11174     for (; itface != faceDomains.end(); ++itface)
11175     {
11176       const std::map<int, int>& domvol = itface->second;
11177       if (!domvol.count(idomain))
11178         continue;
11179       DownIdType face = itface->first;
11180       //MESSAGE(" --- face " << face.cellId);
11181       std::set<int> oldNodes;
11182       oldNodes.clear();
11183       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11184       std::set<int>::iterator itn = oldNodes.begin();
11185       for (; itn != oldNodes.end(); ++itn)
11186       {
11187         int oldId = *itn;
11188         //MESSAGE("     node " << oldId);
11189         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11190         for (int i=0; i<l.ncells; i++)
11191         {
11192           int vtkId = l.cells[i];
11193           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11194           if (!domain.count(anElem))
11195             continue;
11196           int vtkType = grid->GetCellType(vtkId);
11197           int downId = grid->CellIdToDownId(vtkId);
11198           if (downId < 0)
11199           {
11200             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11201             continue; // not OK at this stage of the algorithm:
11202             //no cells created after BuildDownWardConnectivity
11203           }
11204           DownIdType aCell(downId, vtkType);
11205           cellDomains[aCell][idomain] = vtkId;
11206           celldom[vtkId] = idomain;
11207           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11208         }
11209       }
11210     }
11211   }
11212
11213   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11214   //     for each shared face, get the nodes
11215   //     for each node, for each domain of the face, create a clone of the node
11216
11217   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11218   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11219   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11220
11221   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11222   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11223   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11224
11225   //MESSAGE(".. Duplication of the nodes");
11226   for (int idomain = idom0; idomain < nbDomains; idomain++)
11227   {
11228     itface = faceDomains.begin();
11229     for (; itface != faceDomains.end(); ++itface)
11230     {
11231       const std::map<int, int>& domvol = itface->second;
11232       if (!domvol.count(idomain))
11233         continue;
11234       DownIdType face = itface->first;
11235       //MESSAGE(" --- face " << face.cellId);
11236       std::set<int> oldNodes;
11237       oldNodes.clear();
11238       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11239       std::set<int>::iterator itn = oldNodes.begin();
11240       for (; itn != oldNodes.end(); ++itn)
11241       {
11242         int oldId = *itn;
11243         if (nodeDomains[oldId].empty())
11244         {
11245           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11246           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11247         }
11248         std::map<int, int>::const_iterator itdom = domvol.begin();
11249         for (; itdom != domvol.end(); ++itdom)
11250         {
11251           int idom = itdom->first;
11252           //MESSAGE("         domain " << idom);
11253           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11254           {
11255             if (nodeDomains[oldId].size() >= 2) // a multiple node
11256             {
11257               vector<int> orderedDoms;
11258               //MESSAGE("multiple node " << oldId);
11259               if (mutipleNodes.count(oldId))
11260                 orderedDoms = mutipleNodes[oldId];
11261               else
11262               {
11263                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11264                 for (; it != nodeDomains[oldId].end(); ++it)
11265                   orderedDoms.push_back(it->first);
11266               }
11267               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11268               //stringstream txt;
11269               //for (int i=0; i<orderedDoms.size(); i++)
11270               //  txt << orderedDoms[i] << " ";
11271               //MESSAGE("orderedDoms " << txt.str());
11272               mutipleNodes[oldId] = orderedDoms;
11273             }
11274             double *coords = grid->GetPoint(oldId);
11275             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11276             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11277             int newId = newNode->GetVtkID();
11278             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11279             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11280           }
11281         }
11282       }
11283     }
11284   }
11285
11286   //MESSAGE(".. Creation of elements");
11287   for (int idomain = idom0; idomain < nbDomains; idomain++)
11288   {
11289     itface = faceDomains.begin();
11290     for (; itface != faceDomains.end(); ++itface)
11291     {
11292       std::map<int, int> domvol = itface->second;
11293       if (!domvol.count(idomain))
11294         continue;
11295       DownIdType face = itface->first;
11296       //MESSAGE(" --- face " << face.cellId);
11297       std::set<int> oldNodes;
11298       oldNodes.clear();
11299       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11300       int nbMultipleNodes = 0;
11301       std::set<int>::iterator itn = oldNodes.begin();
11302       for (; itn != oldNodes.end(); ++itn)
11303       {
11304         int oldId = *itn;
11305         if (mutipleNodes.count(oldId))
11306           nbMultipleNodes++;
11307       }
11308       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11309       {
11310         //MESSAGE("multiple Nodes detected on a shared face");
11311         int downId = itface->first.cellId;
11312         unsigned char cellType = itface->first.cellType;
11313         // --- shared edge or shared face ?
11314         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11315         {
11316           int nodes[3];
11317           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11318           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11319             if (mutipleNodes.count(nodes[i]))
11320               if (!mutipleNodesToFace.count(nodes[i]))
11321                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11322         }
11323         else // shared face (between two volumes)
11324         {
11325           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11326           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11327           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11328           for (int ie =0; ie < nbEdges; ie++)
11329           {
11330             int nodes[3];
11331             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11332             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11333             {
11334               vector<int> vn0 = mutipleNodes[nodes[0]];
11335               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11336               vector<int> doms;
11337               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11338                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11339                   if ( vn0[i0] == vn1[i1] )
11340                     doms.push_back( vn0[ i0 ]);
11341               if ( doms.size() > 2 )
11342               {
11343                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11344                 double *coords = grid->GetPoint(nodes[0]);
11345                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11346                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11347                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11348                 gp_Pnt gref;
11349                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11350                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11351                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11352                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11353                 for ( size_t id = 0; id < doms.size(); id++ )
11354                 {
11355                   int idom = doms[id];
11356                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11357                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11358                   {
11359                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11360                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11361                     if (domain.count(elem))
11362                     {
11363                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11364                       domvol[idom] = (SMDS_MeshVolume*) svol;
11365                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11366                       double values[3] = { 0,0,0 };
11367                       vtkIdType npts = 0;
11368                       vtkIdType* pts = 0;
11369                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11370                       for ( vtkIdType i = 0; i < npts; ++i )
11371                       {
11372                         double *coords = grid->GetPoint( pts[i] );
11373                         for ( int j = 0; j < 3; ++j )
11374                           values[j] += coords[j] / npts;
11375                       }
11376                       if ( id == 0 )
11377                       {
11378                         gref.SetCoord( values[0], values[1], values[2] );
11379                         angleDom[idom] = 0;
11380                       }
11381                       else
11382                       {
11383                         gp_Pnt g( values[0], values[1], values[2] );
11384                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11385                         //MESSAGE("  angle=" << angleDom[idom]);
11386                       }
11387                       break;
11388                     }
11389                   }
11390                 }
11391                 map<double, int> sortedDom; // sort domains by angle
11392                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11393                   sortedDom[ia->second] = ia->first;
11394                 vector<int> vnodes;
11395                 vector<int> vdom;
11396                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11397                 {
11398                   vdom.push_back(ib->second);
11399                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11400                 }
11401                 for (int ino = 0; ino < nbNodes; ino++)
11402                   vnodes.push_back(nodes[ino]);
11403                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11404               }
11405             }
11406           }
11407         }
11408       }
11409     }
11410   }
11411
11412   // --- iterate on shared faces (volumes to modify, face to extrude)
11413   //     get node id's of the face (id SMDS = id VTK)
11414   //     create flat element with old and new nodes if requested
11415
11416   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11417   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11418
11419   std::map<int, std::map<long,int> > nodeQuadDomains;
11420   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11421
11422   //MESSAGE(".. Creation of elements: simple junction");
11423   if (createJointElems)
11424   {
11425     string joints2DName = "joints2D";
11426     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11427     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11428     string joints3DName = "joints3D";
11429     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11430     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11431
11432     itface = faceDomains.begin();
11433     for (; itface != faceDomains.end(); ++itface)
11434     {
11435       DownIdType face = itface->first;
11436       std::set<int> oldNodes;
11437       std::set<int>::iterator itn;
11438       oldNodes.clear();
11439       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11440
11441       std::map<int, int> domvol = itface->second;
11442       std::map<int, int>::iterator itdom = domvol.begin();
11443       int dom1 = itdom->first;
11444       int vtkVolId = itdom->second;
11445       itdom++;
11446       int dom2 = itdom->first;
11447       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11448                                                        nodeQuadDomains);
11449       stringstream grpname;
11450       grpname << "j_";
11451       if (dom1 < dom2)
11452         grpname << dom1 << "_" << dom2;
11453       else
11454         grpname << dom2 << "_" << dom1;
11455       string namegrp = grpname.str();
11456       if (!mapOfJunctionGroups.count(namegrp))
11457         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11458       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11459       if (sgrp)
11460         sgrp->Add(vol->GetID());
11461       if (vol->GetType() == SMDSAbs_Volume)
11462         joints3DGrp->Add(vol->GetID());
11463       else if (vol->GetType() == SMDSAbs_Face)
11464         joints2DGrp->Add(vol->GetID());
11465     }
11466   }
11467
11468   // --- create volumes on multiple domain intersection if requested
11469   //     iterate on mutipleNodesToFace
11470   //     iterate on edgesMultiDomains
11471
11472   //MESSAGE(".. Creation of elements: multiple junction");
11473   if (createJointElems)
11474   {
11475     // --- iterate on mutipleNodesToFace
11476
11477     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11478     for (; itn != mutipleNodesToFace.end(); ++itn)
11479     {
11480       int node = itn->first;
11481       vector<int> orderDom = itn->second;
11482       vector<vtkIdType> orderedNodes;
11483       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11484         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11485       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11486
11487       stringstream grpname;
11488       grpname << "m2j_";
11489       grpname << 0 << "_" << 0;
11490       string namegrp = grpname.str();
11491       if (!mapOfJunctionGroups.count(namegrp))
11492         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11493       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11494       if (sgrp)
11495         sgrp->Add(face->GetID());
11496     }
11497
11498     // --- iterate on edgesMultiDomains
11499
11500     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11501     for (; ite != edgesMultiDomains.end(); ++ite)
11502     {
11503       vector<int> nodes = ite->first;
11504       vector<int> orderDom = ite->second;
11505       vector<vtkIdType> orderedNodes;
11506       if (nodes.size() == 2)
11507       {
11508         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11509         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11510           if ( orderDom.size() == 3 )
11511             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11512               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11513           else
11514             for (int idom = orderDom.size()-1; idom >=0; idom--)
11515               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11516         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11517
11518         string namegrp = "jointsMultiples";
11519         if (!mapOfJunctionGroups.count(namegrp))
11520           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11521         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11522         if (sgrp)
11523           sgrp->Add(vol->GetID());
11524       }
11525       else
11526       {
11527         //INFOS("Quadratic multiple joints not implemented");
11528         // TODO quadratic nodes
11529       }
11530     }
11531   }
11532
11533   // --- list the explicit faces and edges of the mesh that need to be modified,
11534   //     i.e. faces and edges built with one or more duplicated nodes.
11535   //     associate these faces or edges to their corresponding domain.
11536   //     only the first domain found is kept when a face or edge is shared
11537
11538   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11539   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11540   faceOrEdgeDom.clear();
11541   feDom.clear();
11542
11543   //MESSAGE(".. Modification of elements");
11544   for (int idomain = idom0; idomain < nbDomains; idomain++)
11545   {
11546     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11547     for (; itnod != nodeDomains.end(); ++itnod)
11548     {
11549       int oldId = itnod->first;
11550       //MESSAGE("     node " << oldId);
11551       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11552       for (int i = 0; i < l.ncells; i++)
11553       {
11554         int vtkId = l.cells[i];
11555         int vtkType = grid->GetCellType(vtkId);
11556         int downId = grid->CellIdToDownId(vtkId);
11557         if (downId < 0)
11558           continue; // new cells: not to be modified
11559         DownIdType aCell(downId, vtkType);
11560         int volParents[1000];
11561         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11562         for (int j = 0; j < nbvol; j++)
11563           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11564             if (!feDom.count(vtkId))
11565             {
11566               feDom[vtkId] = idomain;
11567               faceOrEdgeDom[aCell] = emptyMap;
11568               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11569               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11570               //        << " type " << vtkType << " downId " << downId);
11571             }
11572       }
11573     }
11574   }
11575
11576   // --- iterate on shared faces (volumes to modify, face to extrude)
11577   //     get node id's of the face
11578   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11579
11580   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11581   for (int m=0; m<3; m++)
11582   {
11583     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11584     itface = (*amap).begin();
11585     for (; itface != (*amap).end(); ++itface)
11586     {
11587       DownIdType face = itface->first;
11588       std::set<int> oldNodes;
11589       std::set<int>::iterator itn;
11590       oldNodes.clear();
11591       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11592       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11593       std::map<int, int> localClonedNodeIds;
11594
11595       std::map<int, int> domvol = itface->second;
11596       std::map<int, int>::iterator itdom = domvol.begin();
11597       for (; itdom != domvol.end(); ++itdom)
11598       {
11599         int idom = itdom->first;
11600         int vtkVolId = itdom->second;
11601         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11602         localClonedNodeIds.clear();
11603         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11604         {
11605           int oldId = *itn;
11606           if (nodeDomains[oldId].count(idom))
11607           {
11608             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11609             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11610           }
11611         }
11612         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11613       }
11614     }
11615   }
11616
11617   // Remove empty groups (issue 0022812)
11618   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11619   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11620   {
11621     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11622       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11623   }
11624
11625   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11626   grid->DeleteLinks();
11627
11628   CHRONOSTOP(50);
11629   counters::stats();
11630   return true;
11631 }
11632
11633 /*!
11634  * \brief Double nodes on some external faces and create flat elements.
11635  * Flat elements are mainly used by some types of mechanic calculations.
11636  *
11637  * Each group of the list must be constituted of faces.
11638  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11639  * @param theElems - list of groups of faces, where a group of faces is a set of
11640  * SMDS_MeshElements sorted by Id.
11641  * @return TRUE if operation has been completed successfully, FALSE otherwise
11642  */
11643 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11644 {
11645   // MESSAGE("-------------------------------------------------");
11646   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11647   // MESSAGE("-------------------------------------------------");
11648
11649   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11650
11651   // --- For each group of faces
11652   //     duplicate the nodes, create a flat element based on the face
11653   //     replace the nodes of the faces by their clones
11654
11655   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11656   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11657   clonedNodes.clear();
11658   intermediateNodes.clear();
11659   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11660   mapOfJunctionGroups.clear();
11661
11662   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11663   {
11664     const TIDSortedElemSet&           domain = theElems[idom];
11665     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11666     for ( ; elemItr != domain.end(); ++elemItr )
11667     {
11668       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11669       if (!aFace)
11670         continue;
11671       // MESSAGE("aFace=" << aFace->GetID());
11672       bool isQuad = aFace->IsQuadratic();
11673       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11674
11675       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11676
11677       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11678       while (nodeIt->more())
11679       {
11680         const SMDS_MeshNode* node = nodeIt->next();
11681         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11682         if (isMedium)
11683           ln2.push_back(node);
11684         else
11685           ln0.push_back(node);
11686
11687         const SMDS_MeshNode* clone = 0;
11688         if (!clonedNodes.count(node))
11689         {
11690           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11691           copyPosition( node, clone );
11692           clonedNodes[node] = clone;
11693         }
11694         else
11695           clone = clonedNodes[node];
11696
11697         if (isMedium)
11698           ln3.push_back(clone);
11699         else
11700           ln1.push_back(clone);
11701
11702         const SMDS_MeshNode* inter = 0;
11703         if (isQuad && (!isMedium))
11704         {
11705           if (!intermediateNodes.count(node))
11706           {
11707             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11708             copyPosition( node, inter );
11709             intermediateNodes[node] = inter;
11710           }
11711           else
11712             inter = intermediateNodes[node];
11713           ln4.push_back(inter);
11714         }
11715       }
11716
11717       // --- extrude the face
11718
11719       vector<const SMDS_MeshNode*> ln;
11720       SMDS_MeshVolume* vol = 0;
11721       vtkIdType aType = aFace->GetVtkType();
11722       switch (aType)
11723       {
11724       case VTK_TRIANGLE:
11725         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11726         // MESSAGE("vol prism " << vol->GetID());
11727         ln.push_back(ln1[0]);
11728         ln.push_back(ln1[1]);
11729         ln.push_back(ln1[2]);
11730         break;
11731       case VTK_QUAD:
11732         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11733         // MESSAGE("vol hexa " << vol->GetID());
11734         ln.push_back(ln1[0]);
11735         ln.push_back(ln1[1]);
11736         ln.push_back(ln1[2]);
11737         ln.push_back(ln1[3]);
11738         break;
11739       case VTK_QUADRATIC_TRIANGLE:
11740         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11741                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11742         // MESSAGE("vol quad prism " << vol->GetID());
11743         ln.push_back(ln1[0]);
11744         ln.push_back(ln1[1]);
11745         ln.push_back(ln1[2]);
11746         ln.push_back(ln3[0]);
11747         ln.push_back(ln3[1]);
11748         ln.push_back(ln3[2]);
11749         break;
11750       case VTK_QUADRATIC_QUAD:
11751         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11752         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11753         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11754         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11755                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11756                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11757         // MESSAGE("vol quad hexa " << vol->GetID());
11758         ln.push_back(ln1[0]);
11759         ln.push_back(ln1[1]);
11760         ln.push_back(ln1[2]);
11761         ln.push_back(ln1[3]);
11762         ln.push_back(ln3[0]);
11763         ln.push_back(ln3[1]);
11764         ln.push_back(ln3[2]);
11765         ln.push_back(ln3[3]);
11766         break;
11767       case VTK_POLYGON:
11768         break;
11769       default:
11770         break;
11771       }
11772
11773       if (vol)
11774       {
11775         stringstream grpname;
11776         grpname << "jf_";
11777         grpname << idom;
11778         string namegrp = grpname.str();
11779         if (!mapOfJunctionGroups.count(namegrp))
11780           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11781         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11782         if (sgrp)
11783           sgrp->Add(vol->GetID());
11784       }
11785
11786       // --- modify the face
11787
11788       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11789     }
11790   }
11791   return true;
11792 }
11793
11794 /*!
11795  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11796  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11797  *  groups of faces to remove inside the object, (idem edges).
11798  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11799  */
11800 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11801                                       const TopoDS_Shape&             theShape,
11802                                       SMESH_NodeSearcher*             theNodeSearcher,
11803                                       const char*                     groupName,
11804                                       std::vector<double>&            nodesCoords,
11805                                       std::vector<std::vector<int> >& listOfListOfNodes)
11806 {
11807   // MESSAGE("--------------------------------");
11808   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11809   // MESSAGE("--------------------------------");
11810
11811   // --- zone of volumes to remove is given :
11812   //     1 either by a geom shape (one or more vertices) and a radius,
11813   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11814   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11815   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11816   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11817   //     defined by it's name.
11818
11819   SMESHDS_GroupBase* groupDS = 0;
11820   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11821   while ( groupIt->more() )
11822   {
11823     groupDS = 0;
11824     SMESH_Group * group = groupIt->next();
11825     if ( !group ) continue;
11826     groupDS = group->GetGroupDS();
11827     if ( !groupDS || groupDS->IsEmpty() ) continue;
11828     std::string grpName = group->GetName();
11829     //MESSAGE("grpName=" << grpName);
11830     if (grpName == groupName)
11831       break;
11832     else
11833       groupDS = 0;
11834   }
11835
11836   bool isNodeGroup = false;
11837   bool isNodeCoords = false;
11838   if (groupDS)
11839   {
11840     if (groupDS->GetType() != SMDSAbs_Node)
11841       return;
11842     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11843   }
11844
11845   if (nodesCoords.size() > 0)
11846     isNodeCoords = true; // a list o nodes given by their coordinates
11847   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11848
11849   // --- define groups to build
11850
11851   // --- group of SMDS volumes
11852   string grpvName = groupName;
11853   grpvName += "_vol";
11854   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11855   if (!grp)
11856   {
11857     MESSAGE("group not created " << grpvName);
11858     return;
11859   }
11860   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11861
11862   // --- group of SMDS faces on the skin
11863   string grpsName = groupName;
11864   grpsName += "_skin";
11865   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11866   if (!grps)
11867   {
11868     MESSAGE("group not created " << grpsName);
11869     return;
11870   }
11871   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11872
11873   // --- group of SMDS faces internal (several shapes)
11874   string grpiName = groupName;
11875   grpiName += "_internalFaces";
11876   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11877   if (!grpi)
11878   {
11879     MESSAGE("group not created " << grpiName);
11880     return;
11881   }
11882   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11883
11884   // --- group of SMDS faces internal (several shapes)
11885   string grpeiName = groupName;
11886   grpeiName += "_internalEdges";
11887   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11888   if (!grpei)
11889   {
11890     MESSAGE("group not created " << grpeiName);
11891     return;
11892   }
11893   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11894
11895   // --- build downward connectivity
11896
11897   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11898   meshDS->BuildDownWardConnectivity(true);
11899   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11900
11901   // --- set of volumes detected inside
11902
11903   std::set<int> setOfInsideVol;
11904   std::set<int> setOfVolToCheck;
11905
11906   std::vector<gp_Pnt> gpnts;
11907   gpnts.clear();
11908
11909   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11910   {
11911     //MESSAGE("group of nodes provided");
11912     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11913     while ( elemIt->more() )
11914     {
11915       const SMDS_MeshElement* elem = elemIt->next();
11916       if (!elem)
11917         continue;
11918       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11919       if (!node)
11920         continue;
11921       SMDS_MeshElement* vol = 0;
11922       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11923       while (volItr->more())
11924       {
11925         vol = (SMDS_MeshElement*)volItr->next();
11926         setOfInsideVol.insert(vol->GetVtkID());
11927         sgrp->Add(vol->GetID());
11928       }
11929     }
11930   }
11931   else if (isNodeCoords)
11932   {
11933     //MESSAGE("list of nodes coordinates provided");
11934     size_t i = 0;
11935     int k = 0;
11936     while ( i < nodesCoords.size()-2 )
11937     {
11938       double x = nodesCoords[i++];
11939       double y = nodesCoords[i++];
11940       double z = nodesCoords[i++];
11941       gp_Pnt p = gp_Pnt(x, y ,z);
11942       gpnts.push_back(p);
11943       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11944       k++;
11945     }
11946   }
11947   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11948   {
11949     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11950     TopTools_IndexedMapOfShape vertexMap;
11951     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11952     gp_Pnt p = gp_Pnt(0,0,0);
11953     if (vertexMap.Extent() < 1)
11954       return;
11955
11956     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11957     {
11958       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11959       p = BRep_Tool::Pnt(vertex);
11960       gpnts.push_back(p);
11961       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11962     }
11963   }
11964
11965   if (gpnts.size() > 0)
11966   {
11967     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11968     //MESSAGE("startNode->nodeId " << nodeId);
11969
11970     double radius2 = radius*radius;
11971     //MESSAGE("radius2 " << radius2);
11972
11973     // --- volumes on start node
11974
11975     setOfVolToCheck.clear();
11976     SMDS_MeshElement* startVol = 0;
11977     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11978     while (volItr->more())
11979     {
11980       startVol = (SMDS_MeshElement*)volItr->next();
11981       setOfVolToCheck.insert(startVol->GetVtkID());
11982     }
11983     if (setOfVolToCheck.empty())
11984     {
11985       MESSAGE("No volumes found");
11986       return;
11987     }
11988
11989     // --- starting with central volumes then their neighbors, check if they are inside
11990     //     or outside the domain, until no more new neighbor volume is inside.
11991     //     Fill the group of inside volumes
11992
11993     std::map<int, double> mapOfNodeDistance2;
11994     mapOfNodeDistance2.clear();
11995     std::set<int> setOfOutsideVol;
11996     while (!setOfVolToCheck.empty())
11997     {
11998       std::set<int>::iterator it = setOfVolToCheck.begin();
11999       int vtkId = *it;
12000       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12001       bool volInside = false;
12002       vtkIdType npts = 0;
12003       vtkIdType* pts = 0;
12004       grid->GetCellPoints(vtkId, npts, pts);
12005       for (int i=0; i<npts; i++)
12006       {
12007         double distance2 = 0;
12008         if (mapOfNodeDistance2.count(pts[i]))
12009         {
12010           distance2 = mapOfNodeDistance2[pts[i]];
12011           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12012         }
12013         else
12014         {
12015           double *coords = grid->GetPoint(pts[i]);
12016           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12017           distance2 = 1.E40;
12018           for ( size_t j = 0; j < gpnts.size(); j++ )
12019           {
12020             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12021             if (d2 < distance2)
12022             {
12023               distance2 = d2;
12024               if (distance2 < radius2)
12025                 break;
12026             }
12027           }
12028           mapOfNodeDistance2[pts[i]] = distance2;
12029           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12030         }
12031         if (distance2 < radius2)
12032         {
12033           volInside = true; // one or more nodes inside the domain
12034           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12035           break;
12036         }
12037       }
12038       if (volInside)
12039       {
12040         setOfInsideVol.insert(vtkId);
12041         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12042         int neighborsVtkIds[NBMAXNEIGHBORS];
12043         int downIds[NBMAXNEIGHBORS];
12044         unsigned char downTypes[NBMAXNEIGHBORS];
12045         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12046         for (int n = 0; n < nbNeighbors; n++)
12047           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12048             setOfVolToCheck.insert(neighborsVtkIds[n]);
12049       }
12050       else
12051       {
12052         setOfOutsideVol.insert(vtkId);
12053         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12054       }
12055       setOfVolToCheck.erase(vtkId);
12056     }
12057   }
12058
12059   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12060   //     If yes, add the volume to the inside set
12061
12062   bool addedInside = true;
12063   std::set<int> setOfVolToReCheck;
12064   while (addedInside)
12065   {
12066     //MESSAGE(" --------------------------- re check");
12067     addedInside = false;
12068     std::set<int>::iterator itv = setOfInsideVol.begin();
12069     for (; itv != setOfInsideVol.end(); ++itv)
12070     {
12071       int vtkId = *itv;
12072       int neighborsVtkIds[NBMAXNEIGHBORS];
12073       int downIds[NBMAXNEIGHBORS];
12074       unsigned char downTypes[NBMAXNEIGHBORS];
12075       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12076       for (int n = 0; n < nbNeighbors; n++)
12077         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12078           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12079     }
12080     setOfVolToCheck = setOfVolToReCheck;
12081     setOfVolToReCheck.clear();
12082     while  (!setOfVolToCheck.empty())
12083     {
12084       std::set<int>::iterator it = setOfVolToCheck.begin();
12085       int vtkId = *it;
12086       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12087       {
12088         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12089         int countInside = 0;
12090         int neighborsVtkIds[NBMAXNEIGHBORS];
12091         int downIds[NBMAXNEIGHBORS];
12092         unsigned char downTypes[NBMAXNEIGHBORS];
12093         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12094         for (int n = 0; n < nbNeighbors; n++)
12095           if (setOfInsideVol.count(neighborsVtkIds[n]))
12096             countInside++;
12097         //MESSAGE("countInside " << countInside);
12098         if (countInside > 1)
12099         {
12100           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12101           setOfInsideVol.insert(vtkId);
12102           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12103           addedInside = true;
12104         }
12105         else
12106           setOfVolToReCheck.insert(vtkId);
12107       }
12108       setOfVolToCheck.erase(vtkId);
12109     }
12110   }
12111
12112   // --- map of Downward faces at the boundary, inside the global volume
12113   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12114   //     fill group of SMDS faces inside the volume (when several volume shapes)
12115   //     fill group of SMDS faces on the skin of the global volume (if skin)
12116
12117   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12118   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12119   std::set<int>::iterator it = setOfInsideVol.begin();
12120   for (; it != setOfInsideVol.end(); ++it)
12121   {
12122     int vtkId = *it;
12123     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12124     int neighborsVtkIds[NBMAXNEIGHBORS];
12125     int downIds[NBMAXNEIGHBORS];
12126     unsigned char downTypes[NBMAXNEIGHBORS];
12127     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12128     for (int n = 0; n < nbNeighbors; n++)
12129     {
12130       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12131       if (neighborDim == 3)
12132       {
12133         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12134         {
12135           DownIdType face(downIds[n], downTypes[n]);
12136           boundaryFaces[face] = vtkId;
12137         }
12138         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12139         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12140         if (vtkFaceId >= 0)
12141         {
12142           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12143           // find also the smds edges on this face
12144           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12145           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12146           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12147           for (int i = 0; i < nbEdges; i++)
12148           {
12149             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12150             if (vtkEdgeId >= 0)
12151               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12152           }
12153         }
12154       }
12155       else if (neighborDim == 2) // skin of the volume
12156       {
12157         DownIdType face(downIds[n], downTypes[n]);
12158         skinFaces[face] = vtkId;
12159         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12160         if (vtkFaceId >= 0)
12161           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12162       }
12163     }
12164   }
12165
12166   // --- identify the edges constituting the wire of each subshape on the skin
12167   //     define polylines with the nodes of edges, equivalent to wires
12168   //     project polylines on subshapes, and partition, to get geom faces
12169
12170   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12171   std::set<int> emptySet;
12172   emptySet.clear();
12173   std::set<int> shapeIds;
12174
12175   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12176   while (itelem->more())
12177   {
12178     const SMDS_MeshElement *elem = itelem->next();
12179     int shapeId = elem->getshapeId();
12180     int   vtkId = elem->GetVtkID();
12181     if (!shapeIdToVtkIdSet.count(shapeId))
12182     {
12183       shapeIdToVtkIdSet[shapeId] = emptySet;
12184       shapeIds.insert(shapeId);
12185     }
12186     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12187   }
12188
12189   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12190   std::set<DownIdType, DownIdCompare> emptyEdges;
12191   emptyEdges.clear();
12192
12193   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12194   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12195   {
12196     int shapeId = itShape->first;
12197     //MESSAGE(" --- Shape ID --- "<< shapeId);
12198     shapeIdToEdges[shapeId] = emptyEdges;
12199
12200     std::vector<int> nodesEdges;
12201
12202     std::set<int>::iterator its = itShape->second.begin();
12203     for (; its != itShape->second.end(); ++its)
12204     {
12205       int vtkId = *its;
12206       //MESSAGE("     " << vtkId);
12207       int neighborsVtkIds[NBMAXNEIGHBORS];
12208       int downIds[NBMAXNEIGHBORS];
12209       unsigned char downTypes[NBMAXNEIGHBORS];
12210       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12211       for (int n = 0; n < nbNeighbors; n++)
12212       {
12213         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12214           continue;
12215         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12216         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12217         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12218         {
12219           DownIdType edge(downIds[n], downTypes[n]);
12220           if (!shapeIdToEdges[shapeId].count(edge))
12221           {
12222             shapeIdToEdges[shapeId].insert(edge);
12223             int vtkNodeId[3];
12224             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12225             nodesEdges.push_back(vtkNodeId[0]);
12226             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12227             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12228           }
12229         }
12230       }
12231     }
12232
12233     std::list<int> order;
12234     order.clear();
12235     if (nodesEdges.size() > 0)
12236     {
12237       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12238       nodesEdges[0] = -1;
12239       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12240       nodesEdges[1] = -1; // do not reuse this edge
12241       bool found = true;
12242       while (found)
12243       {
12244         int nodeTofind = order.back(); // try first to push back
12245         int i = 0;
12246         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12247           if (nodesEdges[i] == nodeTofind)
12248             break;
12249         if ( i == (int) nodesEdges.size() )
12250           found = false; // no follower found on back
12251         else
12252         {
12253           if (i%2) // odd ==> use the previous one
12254             if (nodesEdges[i-1] < 0)
12255               found = false;
12256             else
12257             {
12258               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12259               nodesEdges[i-1] = -1;
12260             }
12261           else // even ==> use the next one
12262             if (nodesEdges[i+1] < 0)
12263               found = false;
12264             else
12265             {
12266               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12267               nodesEdges[i+1] = -1;
12268             }
12269         }
12270         if (found)
12271           continue;
12272         // try to push front
12273         found = true;
12274         nodeTofind = order.front(); // try to push front
12275         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12276           if ( nodesEdges[i] == nodeTofind )
12277             break;
12278         if ( i == (int)nodesEdges.size() )
12279         {
12280           found = false; // no predecessor found on front
12281           continue;
12282         }
12283         if (i%2) // odd ==> use the previous one
12284           if (nodesEdges[i-1] < 0)
12285             found = false;
12286           else
12287           {
12288             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12289             nodesEdges[i-1] = -1;
12290           }
12291         else // even ==> use the next one
12292           if (nodesEdges[i+1] < 0)
12293             found = false;
12294           else
12295           {
12296             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12297             nodesEdges[i+1] = -1;
12298           }
12299       }
12300     }
12301
12302
12303     std::vector<int> nodes;
12304     nodes.push_back(shapeId);
12305     std::list<int>::iterator itl = order.begin();
12306     for (; itl != order.end(); itl++)
12307     {
12308       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12309       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12310     }
12311     listOfListOfNodes.push_back(nodes);
12312   }
12313
12314   //     partition geom faces with blocFissure
12315   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12316   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12317
12318   return;
12319 }
12320
12321
12322 //================================================================================
12323 /*!
12324  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12325  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12326  * \return TRUE if operation has been completed successfully, FALSE otherwise
12327  */
12328 //================================================================================
12329
12330 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12331 {
12332   // iterates on volume elements and detect all free faces on them
12333   SMESHDS_Mesh* aMesh = GetMeshDS();
12334   if (!aMesh)
12335     return false;
12336
12337   ElemFeatures faceType( SMDSAbs_Face );
12338   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12339   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12340   while(vIt->more())
12341   {
12342     const SMDS_MeshVolume* volume = vIt->next();
12343     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12344     vTool.SetExternalNormal();
12345     const int iQuad = volume->IsQuadratic();
12346     faceType.SetQuad( iQuad );
12347     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12348     {
12349       if (!vTool.IsFreeFace(iface))
12350         continue;
12351       nbFree++;
12352       vector<const SMDS_MeshNode *> nodes;
12353       int nbFaceNodes = vTool.NbFaceNodes(iface);
12354       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12355       int inode = 0;
12356       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12357         nodes.push_back(faceNodes[inode]);
12358
12359       if (iQuad) // add medium nodes
12360       {
12361         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12362           nodes.push_back(faceNodes[inode]);
12363         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12364           nodes.push_back(faceNodes[8]);
12365       }
12366       // add new face based on volume nodes
12367       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12368       {
12369         nbExisted++; // face already exists
12370       }
12371       else
12372       {
12373         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12374         nbCreated++;
12375       }
12376     }
12377   }
12378   return ( nbFree == ( nbExisted + nbCreated ));
12379 }
12380
12381 namespace
12382 {
12383   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12384   {
12385     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12386       return n;
12387     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12388   }
12389 }
12390 //================================================================================
12391 /*!
12392  * \brief Creates missing boundary elements
12393  *  \param elements - elements whose boundary is to be checked
12394  *  \param dimension - defines type of boundary elements to create
12395  *  \param group - a group to store created boundary elements in
12396  *  \param targetMesh - a mesh to store created boundary elements in
12397  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12398  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12399  *                                boundary elements will be copied into the targetMesh
12400  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12401  *                                boundary elements will be added into the new group
12402  *  \param aroundElements - if true, elements will be created on boundary of given
12403  *                          elements else, on boundary of the whole mesh.
12404  * \return nb of added boundary elements
12405  */
12406 //================================================================================
12407
12408 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12409                                        Bnd_Dimension           dimension,
12410                                        SMESH_Group*            group/*=0*/,
12411                                        SMESH_Mesh*             targetMesh/*=0*/,
12412                                        bool                    toCopyElements/*=false*/,
12413                                        bool                    toCopyExistingBoundary/*=false*/,
12414                                        bool                    toAddExistingBondary/*= false*/,
12415                                        bool                    aroundElements/*= false*/)
12416 {
12417   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12418   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12419   // hope that all elements are of the same type, do not check them all
12420   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12421     throw SALOME_Exception(LOCALIZED("wrong element type"));
12422
12423   if ( !targetMesh )
12424     toCopyElements = toCopyExistingBoundary = false;
12425
12426   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12427   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12428   int nbAddedBnd = 0;
12429
12430   // editor adding present bnd elements and optionally holding elements to add to the group
12431   SMESH_MeshEditor* presentEditor;
12432   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12433   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12434
12435   SMESH_MesherHelper helper( *myMesh );
12436   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12437   SMDS_VolumeTool vTool;
12438   TIDSortedElemSet avoidSet;
12439   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12440   size_t inode;
12441
12442   typedef vector<const SMDS_MeshNode*> TConnectivity;
12443   TConnectivity tgtNodes;
12444   ElemFeatures elemKind( missType ), elemToCopy;
12445
12446   vector<const SMDS_MeshElement*> presentBndElems;
12447   vector<TConnectivity>           missingBndElems;
12448   vector<int>                     freeFacets;
12449   TConnectivity nodes, elemNodes;
12450
12451   SMDS_ElemIteratorPtr eIt;
12452   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12453   else                  eIt = SMESHUtils::elemSetIterator( elements );
12454
12455   while ( eIt->more() )
12456   {
12457     const SMDS_MeshElement* elem = eIt->next();
12458     const int              iQuad = elem->IsQuadratic();
12459     elemKind.SetQuad( iQuad );
12460
12461     // ------------------------------------------------------------------------------------
12462     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12463     // ------------------------------------------------------------------------------------
12464     presentBndElems.clear();
12465     missingBndElems.clear();
12466     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12467     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12468     {
12469       const SMDS_MeshElement* otherVol = 0;
12470       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12471       {
12472         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12473              ( !aroundElements || elements.count( otherVol )))
12474           continue;
12475         freeFacets.push_back( iface );
12476       }
12477       if ( missType == SMDSAbs_Face )
12478         vTool.SetExternalNormal();
12479       for ( size_t i = 0; i < freeFacets.size(); ++i )
12480       {
12481         int                iface = freeFacets[i];
12482         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12483         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12484         if ( missType == SMDSAbs_Edge ) // boundary edges
12485         {
12486           nodes.resize( 2+iQuad );
12487           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12488           {
12489             for ( size_t j = 0; j < nodes.size(); ++j )
12490               nodes[ j ] = nn[ i+j ];
12491             if ( const SMDS_MeshElement* edge =
12492                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12493               presentBndElems.push_back( edge );
12494             else
12495               missingBndElems.push_back( nodes );
12496           }
12497         }
12498         else // boundary face
12499         {
12500           nodes.clear();
12501           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12502             nodes.push_back( nn[inode] ); // add corner nodes
12503           if (iQuad)
12504             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12505               nodes.push_back( nn[inode] ); // add medium nodes
12506           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12507           if ( iCenter > 0 )
12508             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12509
12510           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12511                                                                SMDSAbs_Face, /*noMedium=*/false ))
12512             presentBndElems.push_back( f );
12513           else
12514             missingBndElems.push_back( nodes );
12515
12516           if ( targetMesh != myMesh )
12517           {
12518             // add 1D elements on face boundary to be added to a new mesh
12519             const SMDS_MeshElement* edge;
12520             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12521             {
12522               if ( iQuad )
12523                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12524               else
12525                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12526               if ( edge && avoidSet.insert( edge ).second )
12527                 presentBndElems.push_back( edge );
12528             }
12529           }
12530         }
12531       }
12532     }
12533     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12534     {
12535       avoidSet.clear(), avoidSet.insert( elem );
12536       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12537                         SMDS_MeshElement::iterator() );
12538       elemNodes.push_back( elemNodes[0] );
12539       nodes.resize( 2 + iQuad );
12540       const int nbLinks = elem->NbCornerNodes();
12541       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12542       {
12543         nodes[0] = elemNodes[iN];
12544         nodes[1] = elemNodes[iN+1+iQuad];
12545         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12546           continue; // not free link
12547
12548         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12549         if ( const SMDS_MeshElement* edge =
12550              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12551           presentBndElems.push_back( edge );
12552         else
12553           missingBndElems.push_back( nodes );
12554       }
12555     }
12556
12557     // ---------------------------------
12558     // 2. Add missing boundary elements
12559     // ---------------------------------
12560     if ( targetMesh != myMesh )
12561       // instead of making a map of nodes in this mesh and targetMesh,
12562       // we create nodes with same IDs.
12563       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12564       {
12565         TConnectivity& srcNodes = missingBndElems[i];
12566         tgtNodes.resize( srcNodes.size() );
12567         for ( inode = 0; inode < srcNodes.size(); ++inode )
12568           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12569         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12570                                                                        missType,
12571                                                                        /*noMedium=*/false))
12572           continue;
12573         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12574         ++nbAddedBnd;
12575       }
12576     else
12577       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12578       {
12579         TConnectivity& nodes = missingBndElems[ i ];
12580         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12581                                                                        missType,
12582                                                                        /*noMedium=*/false))
12583           continue;
12584         SMDS_MeshElement* newElem =
12585           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12586         nbAddedBnd += bool( newElem );
12587
12588         // try to set a new element to a shape
12589         if ( myMesh->HasShapeToMesh() )
12590         {
12591           bool ok = true;
12592           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12593           const size_t nbN = nodes.size() / (iQuad+1 );
12594           for ( inode = 0; inode < nbN && ok; ++inode )
12595           {
12596             pair<int, TopAbs_ShapeEnum> i_stype =
12597               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12598             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12599               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12600           }
12601           if ( ok && mediumShapes.size() > 1 )
12602           {
12603             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12604             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12605             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12606             {
12607               if (( ok = ( stype_i->first != stype_i_0.first )))
12608                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12609                                         aMesh->IndexToShape( stype_i_0.second ));
12610             }
12611           }
12612           if ( ok && mediumShapes.begin()->first == missShapeType )
12613             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12614         }
12615       }
12616
12617     // ----------------------------------
12618     // 3. Copy present boundary elements
12619     // ----------------------------------
12620     if ( toCopyExistingBoundary )
12621       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12622       {
12623         const SMDS_MeshElement* e = presentBndElems[i];
12624         tgtNodes.resize( e->NbNodes() );
12625         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12626           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12627         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12628       }
12629     else // store present elements to add them to a group
12630       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12631       {
12632         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12633       }
12634
12635   } // loop on given elements
12636
12637   // ---------------------------------------------
12638   // 4. Fill group with boundary elements
12639   // ---------------------------------------------
12640   if ( group )
12641   {
12642     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12643       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12644         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12645   }
12646   tgtEditor.myLastCreatedElems.clear();
12647   tgtEditor2.myLastCreatedElems.clear();
12648
12649   // -----------------------
12650   // 5. Copy given elements
12651   // -----------------------
12652   if ( toCopyElements && targetMesh != myMesh )
12653   {
12654     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12655     else                  eIt = SMESHUtils::elemSetIterator( elements );
12656     while (eIt->more())
12657     {
12658       const SMDS_MeshElement* elem = eIt->next();
12659       tgtNodes.resize( elem->NbNodes() );
12660       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12661         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12662       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12663
12664       tgtEditor.myLastCreatedElems.clear();
12665     }
12666   }
12667   return nbAddedBnd;
12668 }
12669
12670 //================================================================================
12671 /*!
12672  * \brief Copy node position and set \a to node on the same geometry
12673  */
12674 //================================================================================
12675
12676 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12677                                      const SMDS_MeshNode* to )
12678 {
12679   if ( !from || !to ) return;
12680
12681   SMDS_PositionPtr pos = from->GetPosition();
12682   if ( !pos || from->getshapeId() < 1 ) return;
12683
12684   switch ( pos->GetTypeOfPosition() )
12685   {
12686   case SMDS_TOP_3DSPACE: break;
12687
12688   case SMDS_TOP_FACE:
12689   {
12690     SMDS_FacePositionPtr fPos = pos;
12691     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12692                                 fPos->GetUParameter(), fPos->GetVParameter() );
12693     break;
12694   }
12695   case SMDS_TOP_EDGE:
12696   {
12697     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12698     SMDS_EdgePositionPtr ePos = pos;
12699     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12700     break;
12701   }
12702   case SMDS_TOP_VERTEX:
12703   {
12704     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12705     break;
12706   }
12707   case SMDS_TOP_UNSPEC:
12708   default:;
12709   }
12710 }