Salome HOME
7466070e46f335978bf01c4b6e44712c5ff055c7
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 //=======================================================================
109 //function : SMESH_MeshEditor
110 //purpose  :
111 //=======================================================================
112
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114   :myMesh( theMesh ) // theMesh may be NULL
115 {
116 }
117
118 //================================================================================
119 /*!
120  * \brief Return mesh DS
121  */
122 //================================================================================
123
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 {
126   return myMesh->GetMeshDS();
127 }
128
129
130 //================================================================================
131 /*!
132  * \brief Clears myLastCreatedNodes and myLastCreatedElems
133  */
134 //================================================================================
135
136 void SMESH_MeshEditor::ClearLastCreated()
137 {
138   SMESHUtils::FreeVector( myLastCreatedElems );
139   SMESHUtils::FreeVector( myLastCreatedNodes );
140 }
141
142 //================================================================================
143 /*!
144  * \brief Initializes members by an existing element
145  *  \param [in] elem - the source element
146  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147  */
148 //================================================================================
149
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
152 {
153   if ( elem )
154   {
155     myType = elem->GetType();
156     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157     {
158       myIsPoly = elem->IsPoly();
159       if ( myIsPoly )
160       {
161         myIsQuad = elem->IsQuadratic();
162         if ( myType == SMDSAbs_Volume && !basicOnly )
163         {
164           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165           myPolyhedQuantities.swap( quant );
166         }
167       }
168     }
169     else if ( myType == SMDSAbs_Ball && !basicOnly )
170     {
171       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
172     }
173   }
174   return *this;
175 }
176
177 //=======================================================================
178 /*!
179  * \brief Add element
180  */
181 //=======================================================================
182
183 SMDS_MeshElement*
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185                              const ElemFeatures&                  features)
186 {
187   SMDS_MeshElement* e = 0;
188   int nbnode = node.size();
189   SMESHDS_Mesh* mesh = GetMeshDS();
190   const int ID = features.myID;
191
192   switch ( features.myType ) {
193   case SMDSAbs_Face:
194     if ( !features.myIsPoly ) {
195       if      (nbnode == 3) {
196         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197         else           e = mesh->AddFace      (node[0], node[1], node[2] );
198       }
199       else if (nbnode == 4) {
200         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
202       }
203       else if (nbnode == 6) {
204         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205                                                node[4], node[5], ID);
206         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
207                                                node[4], node[5] );
208       }
209       else if (nbnode == 7) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211                                                node[4], node[5], node[6], ID);
212         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
213                                                node[4], node[5], node[6] );
214       }
215       else if (nbnode == 8) {
216         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217                                                node[4], node[5], node[6], node[7], ID);
218         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
219                                                node[4], node[5], node[6], node[7] );
220       }
221       else if (nbnode == 9) {
222         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6], node[7], node[8], ID);
224         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
225                                                node[4], node[5], node[6], node[7], node[8] );
226       }
227     }
228     else if ( !features.myIsQuad )
229     {
230       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231       else           e = mesh->AddPolygonalFace      (node    );
232     }
233     else if ( nbnode % 2 == 0 ) // just a protection
234     {
235       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236       else           e = mesh->AddQuadPolygonalFace      (node    );
237     }
238     break;
239
240   case SMDSAbs_Volume:
241     if ( !features.myIsPoly ) {
242       if      (nbnode == 4) {
243         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
245       }
246       else if (nbnode == 5) {
247         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248                                                  node[4], ID);
249         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
250                                                  node[4] );
251       }
252       else if (nbnode == 6) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], ID);
255         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
256                                                  node[4], node[5] );
257       }
258       else if (nbnode == 8) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7], ID);
261         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
262                                                  node[4], node[5], node[6], node[7] );
263       }
264       else if (nbnode == 10) {
265         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], ID);
268         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
269                                                  node[4], node[5], node[6], node[7],
270                                                  node[8], node[9] );
271       }
272       else if (nbnode == 12) {
273         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274                                                  node[4], node[5], node[6], node[7],
275                                                  node[8], node[9], node[10], node[11], ID);
276         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
277                                                  node[4], node[5], node[6], node[7],
278                                                  node[8], node[9], node[10], node[11] );
279       }
280       else if (nbnode == 13) {
281         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282                                                  node[4], node[5], node[6], node[7],
283                                                  node[8], node[9], node[10],node[11],
284                                                  node[12],ID);
285         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
286                                                  node[4], node[5], node[6], node[7],
287                                                  node[8], node[9], node[10],node[11],
288                                                  node[12] );
289       }
290       else if (nbnode == 15) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],node[13],node[14],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12],node[13],node[14] );
299       }
300       else if (nbnode == 20) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],node[15],
305                                                  node[16],node[17],node[18],node[19],ID);
306         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
307                                                  node[4], node[5], node[6], node[7],
308                                                  node[8], node[9], node[10],node[11],
309                                                  node[12],node[13],node[14],node[15],
310                                                  node[16],node[17],node[18],node[19] );
311       }
312       else if (nbnode == 27) {
313         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314                                                  node[4], node[5], node[6], node[7],
315                                                  node[8], node[9], node[10],node[11],
316                                                  node[12],node[13],node[14],node[15],
317                                                  node[16],node[17],node[18],node[19],
318                                                  node[20],node[21],node[22],node[23],
319                                                  node[24],node[25],node[26], ID);
320         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
321                                                  node[4], node[5], node[6], node[7],
322                                                  node[8], node[9], node[10],node[11],
323                                                  node[12],node[13],node[14],node[15],
324                                                  node[16],node[17],node[18],node[19],
325                                                  node[20],node[21],node[22],node[23],
326                                                  node[24],node[25],node[26] );
327       }
328     }
329     else if ( !features.myIsQuad )
330     {
331       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
333     }
334     else
335     {
336       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
338     }
339     break;
340
341   case SMDSAbs_Edge:
342     if ( nbnode == 2 ) {
343       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344       else           e = mesh->AddEdge      (node[0], node[1] );
345     }
346     else if ( nbnode == 3 ) {
347       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
349     }
350     break;
351
352   case SMDSAbs_0DElement:
353     if ( nbnode == 1 ) {
354       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355       else           e = mesh->Add0DElement      (node[0] );
356     }
357     break;
358
359   case SMDSAbs_Node:
360     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
362     break;
363
364   case SMDSAbs_Ball:
365     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
367     break;
368
369   default:;
370   }
371   if ( e ) myLastCreatedElems.push_back( e );
372   return e;
373 }
374
375 //=======================================================================
376 /*!
377  * \brief Add element
378  */
379 //=======================================================================
380
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382                                                const ElemFeatures& features)
383 {
384   vector<const SMDS_MeshNode*> nodes;
385   nodes.reserve( nodeIDs.size() );
386   vector<int>::const_iterator id = nodeIDs.begin();
387   while ( id != nodeIDs.end() ) {
388     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389       nodes.push_back( node );
390     else
391       return 0;
392   }
393   return AddElement( nodes, features );
394 }
395
396 //=======================================================================
397 //function : Remove
398 //purpose  : Remove a node or an element.
399 //           Modify a compute state of sub-meshes which become empty
400 //=======================================================================
401
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403                               const bool         isNodes )
404 {
405   ClearLastCreated();
406
407   SMESHDS_Mesh* aMesh = GetMeshDS();
408   set< SMESH_subMesh *> smmap;
409
410   int removed = 0;
411   list<int>::const_iterator it = theIDs.begin();
412   for ( ; it != theIDs.end(); it++ ) {
413     const SMDS_MeshElement * elem;
414     if ( isNodes )
415       elem = aMesh->FindNode( *it );
416     else
417       elem = aMesh->FindElement( *it );
418     if ( !elem )
419       continue;
420
421     // Notify VERTEX sub-meshes about modification
422     if ( isNodes ) {
423       const SMDS_MeshNode* node = cast2Node( elem );
424       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425         if ( int aShapeID = node->getshapeId() )
426           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
427             smmap.insert( sm );
428     }
429     // Find sub-meshes to notify about modification
430     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431     //     while ( nodeIt->more() ) {
432     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
434     //       if ( aPosition.get() ) {
435     //         if ( int aShapeID = aPosition->GetShapeId() ) {
436     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437     //             smmap.insert( sm );
438     //         }
439     //       }
440     //     }
441
442     // Do remove
443     if ( isNodes )
444       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445     else
446       aMesh->RemoveElement( elem );
447     removed++;
448   }
449
450   // Notify sub-meshes about modification
451   if ( !smmap.empty() ) {
452     set< SMESH_subMesh *>::iterator smIt;
453     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
455   }
456
457   //   // Check if the whole mesh becomes empty
458   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
460
461   return removed;
462 }
463
464 //================================================================================
465 /*!
466  * \brief Create 0D elements on all nodes of the given object.
467  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
468  *                    the all mesh is treated
469  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470  *  \param duplicateElements - to add one more 0D element to a node or not
471  */
472 //================================================================================
473
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475                                                    TIDSortedElemSet&       all0DElems,
476                                                    const bool              duplicateElements )
477 {
478   SMDS_ElemIteratorPtr elemIt;
479   if ( elements.empty() )
480   {
481     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482   }
483   else
484   {
485     elemIt = SMESHUtils::elemSetIterator( elements );
486   }
487
488   while ( elemIt->more() )
489   {
490     const SMDS_MeshElement* e = elemIt->next();
491     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492     while ( nodeIt->more() )
493     {
494       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496       if ( duplicateElements || !it0D->more() )
497       {
498         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499         all0DElems.insert( myLastCreatedElems.back() );
500       }
501       while ( it0D->more() )
502         all0DElems.insert( it0D->next() );
503     }
504   }
505 }
506
507 //=======================================================================
508 //function : FindShape
509 //purpose  : Return an index of the shape theElem is on
510 //           or zero if a shape not found
511 //=======================================================================
512
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
514 {
515   ClearLastCreated();
516
517   SMESHDS_Mesh * aMesh = GetMeshDS();
518   if ( aMesh->ShapeToMesh().IsNull() )
519     return 0;
520
521   int aShapeID = theElem->getshapeId();
522   if ( aShapeID < 1 )
523     return 0;
524
525   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526     if ( sm->Contains( theElem ))
527       return aShapeID;
528
529   if ( theElem->GetType() == SMDSAbs_Node ) {
530     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
531   }
532   else {
533     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
534   }
535
536   TopoDS_Shape aShape; // the shape a node of theElem is on
537   if ( theElem->GetType() != SMDSAbs_Node )
538   {
539     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540     while ( nodeIt->more() ) {
541       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542       if ((aShapeID = node->getshapeId()) > 0) {
543         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544           if ( sm->Contains( theElem ))
545             return aShapeID;
546           if ( aShape.IsNull() )
547             aShape = aMesh->IndexToShape( aShapeID );
548         }
549       }
550     }
551   }
552
553   // None of nodes is on a proper shape,
554   // find the shape among ancestors of aShape on which a node is
555   if ( !aShape.IsNull() ) {
556     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557     for ( ; ancIt.More(); ancIt.Next() ) {
558       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559       if ( sm && sm->Contains( theElem ))
560         return aMesh->ShapeToIndex( ancIt.Value() );
561     }
562   }
563   else
564   {
565     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566     while ( const SMESHDS_SubMesh* sm = smIt->next() )
567       if ( sm->Contains( theElem ))
568         return sm->GetID();
569   }
570
571   return 0;
572 }
573
574 //=======================================================================
575 //function : IsMedium
576 //purpose  :
577 //=======================================================================
578
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
580                                 const SMDSAbs_ElementType typeToCheck)
581 {
582   bool isMedium = false;
583   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584   while (it->more() && !isMedium ) {
585     const SMDS_MeshElement* elem = it->next();
586     isMedium = elem->IsMediumNode(node);
587   }
588   return isMedium;
589 }
590
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose  : Shift nodes in the array corresponded to quadratic triangle
594 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
596
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
598 {
599   const SMDS_MeshNode* nd1 = aNodes[0];
600   aNodes[0] = aNodes[1];
601   aNodes[1] = aNodes[2];
602   aNodes[2] = nd1;
603   const SMDS_MeshNode* nd2 = aNodes[3];
604   aNodes[3] = aNodes[4];
605   aNodes[4] = aNodes[5];
606   aNodes[5] = nd2;
607 }
608
609 //=======================================================================
610 //function : getNodesFromTwoTria
611 //purpose  : 
612 //=======================================================================
613
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615                                 const SMDS_MeshElement * theTria2,
616                                 vector< const SMDS_MeshNode*>& N1,
617                                 vector< const SMDS_MeshNode*>& N2)
618 {
619   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620   if ( N1.size() < 6 ) return false;
621   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622   if ( N2.size() < 6 ) return false;
623
624   int sames[3] = {-1,-1,-1};
625   int nbsames = 0;
626   int i, j;
627   for(i=0; i<3; i++) {
628     for(j=0; j<3; j++) {
629       if(N1[i]==N2[j]) {
630         sames[i] = j;
631         nbsames++;
632         break;
633       }
634     }
635   }
636   if(nbsames!=2) return false;
637   if(sames[0]>-1) {
638     shiftNodesQuadTria(N1);
639     if(sames[1]>-1) {
640       shiftNodesQuadTria(N1);
641     }
642   }
643   i = sames[0] + sames[1] + sames[2];
644   for(; i<2; i++) {
645     shiftNodesQuadTria(N2);
646   }
647   // now we receive following N1 and N2 (using numeration as in the image below)
648   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
649   // i.e. first nodes from both arrays form a new diagonal
650   return true;
651 }
652
653 //=======================================================================
654 //function : InverseDiag
655 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
656 //           but having other common link.
657 //           Return False if args are improper
658 //=======================================================================
659
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661                                     const SMDS_MeshElement * theTria2 )
662 {
663   ClearLastCreated();
664
665   if ( !theTria1 || !theTria2 ||
666        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668        theTria1->GetType() != SMDSAbs_Face ||
669        theTria2->GetType() != SMDSAbs_Face )
670     return false;
671
672   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673       (theTria2->GetEntityType() == SMDSEntity_Triangle))
674   {
675     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
677     //    |/ |                                         | \|
678     //  B +--+ 2                                     B +--+ 2
679
680     // put nodes in array and find out indices of the same ones
681     const SMDS_MeshNode* aNodes [6];
682     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
683     int i = 0;
684     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685     while ( it->more() ) {
686       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
687
688       if ( i > 2 ) // theTria2
689         // find same node of theTria1
690         for ( int j = 0; j < 3; j++ )
691           if ( aNodes[ i ] == aNodes[ j ]) {
692             sameInd[ j ] = i;
693             sameInd[ i ] = j;
694             break;
695           }
696       // next
697       i++;
698       if ( i == 3 ) {
699         if ( it->more() )
700           return false; // theTria1 is not a triangle
701         it = theTria2->nodesIterator();
702       }
703       if ( i == 6 && it->more() )
704         return false; // theTria2 is not a triangle
705     }
706
707     // find indices of 1,2 and of A,B in theTria1
708     int iA = -1, iB = 0, i1 = 0, i2 = 0;
709     for ( i = 0; i < 6; i++ ) {
710       if ( sameInd [ i ] == -1 ) {
711         if ( i < 3 ) i1 = i;
712         else         i2 = i;
713       }
714       else if (i < 3) {
715         if ( iA >= 0) iB = i;
716         else          iA = i;
717       }
718     }
719     // nodes 1 and 2 should not be the same
720     if ( aNodes[ i1 ] == aNodes[ i2 ] )
721       return false;
722
723     // theTria1: A->2
724     aNodes[ iA ] = aNodes[ i2 ];
725     // theTria2: B->1
726     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
727
728     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
730
731     return true;
732
733   } // end if(F1 && F2)
734
735   // check case of quadratic faces
736   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
738     return false;
739   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
741     return false;
742
743   //       5
744   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
746   //    |   / |
747   //  7 +  +  + 6
748   //    | /9  |
749   //    |/    |
750   //  4 +--+--+ 3
751   //       8
752
753   vector< const SMDS_MeshNode* > N1;
754   vector< const SMDS_MeshNode* > N2;
755   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
756     return false;
757   // now we receive following N1 and N2 (using numeration as above image)
758   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
759   // i.e. first nodes from both arrays determ new diagonal
760
761   vector< const SMDS_MeshNode*> N1new( N1.size() );
762   vector< const SMDS_MeshNode*> N2new( N2.size() );
763   N1new.back() = N1.back(); // central node of biquadratic
764   N2new.back() = N2.back();
765   N1new[0] = N1[0];  N2new[0] = N1[0];
766   N1new[1] = N2[0];  N2new[1] = N1[1];
767   N1new[2] = N2[1];  N2new[2] = N2[0];
768   N1new[3] = N1[4];  N2new[3] = N1[3];
769   N1new[4] = N2[3];  N2new[4] = N2[5];
770   N1new[5] = N1[5];  N2new[5] = N1[4];
771   // change nodes in faces
772   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
774
775   // move the central node of biquadratic triangle
776   SMESH_MesherHelper helper( *GetMesh() );
777   for ( int is2nd = 0; is2nd < 2; ++is2nd )
778   {
779     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
780     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781     if ( nodes.size() < 7 )
782       continue;
783     helper.SetSubShape( tria->getshapeId() );
784     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
785     gp_Pnt xyz;
786     if ( F.IsNull() )
787     {
788       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789               SMESH_NodeXYZ( nodes[4] ) +
790               SMESH_NodeXYZ( nodes[5] )) / 3.;
791     }
792     else
793     {
794       bool checkUV;
795       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
798       TopLoc_Location loc;
799       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800       xyz = S->Value( uv.X(), uv.Y() );
801       xyz.Transform( loc );
802       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
803            nodes[6]->getshapeId() > 0 )
804         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
805     }
806     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
807   }
808   return true;
809 }
810
811 //=======================================================================
812 //function : findTriangles
813 //purpose  : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
815
816 static bool findTriangles(const SMDS_MeshNode *    theNode1,
817                           const SMDS_MeshNode *    theNode2,
818                           const SMDS_MeshElement*& theTria1,
819                           const SMDS_MeshElement*& theTria2)
820 {
821   if ( !theNode1 || !theNode2 ) return false;
822
823   theTria1 = theTria2 = 0;
824
825   set< const SMDS_MeshElement* > emap;
826   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
827   while (it->more()) {
828     const SMDS_MeshElement* elem = it->next();
829     if ( elem->NbCornerNodes() == 3 )
830       emap.insert( elem );
831   }
832   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
833   while (it->more()) {
834     const SMDS_MeshElement* elem = it->next();
835     if ( emap.count( elem )) {
836       if ( !theTria1 )
837       {
838         theTria1 = elem;
839       }
840       else  
841       {
842         theTria2 = elem;
843         // theTria1 must be element with minimum ID
844         if ( theTria2->GetID() < theTria1->GetID() )
845           std::swap( theTria2, theTria1 );
846         return true;
847       }
848     }
849   }
850   return false;
851 }
852
853 //=======================================================================
854 //function : InverseDiag
855 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
856 //           with ones built on the same 4 nodes but having other common link.
857 //           Return false if proper faces not found
858 //=======================================================================
859
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861                                     const SMDS_MeshNode * theNode2)
862 {
863   ClearLastCreated();
864
865   const SMDS_MeshElement *tr1, *tr2;
866   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
867     return false;
868
869   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
871     return false;
872
873   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
875
876     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
878     //    |/ |                                    | \|
879     //  B +--+ 2                                B +--+ 2
880
881     // put nodes in array
882     // and find indices of 1,2 and of A in tr1 and of B in tr2
883     int i, iA1 = 0, i1 = 0;
884     const SMDS_MeshNode* aNodes1 [3];
885     SMDS_ElemIteratorPtr it;
886     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888       if ( aNodes1[ i ] == theNode1 )
889         iA1 = i; // node A in tr1
890       else if ( aNodes1[ i ] != theNode2 )
891         i1 = i;  // node 1
892     }
893     int iB2 = 0, i2 = 0;
894     const SMDS_MeshNode* aNodes2 [3];
895     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897       if ( aNodes2[ i ] == theNode2 )
898         iB2 = i; // node B in tr2
899       else if ( aNodes2[ i ] != theNode1 )
900         i2 = i;  // node 2
901     }
902
903     // nodes 1 and 2 should not be the same
904     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
905       return false;
906
907     // tr1: A->2
908     aNodes1[ iA1 ] = aNodes2[ i2 ];
909     // tr2: B->1
910     aNodes2[ iB2 ] = aNodes1[ i1 ];
911
912     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
914
915     return true;
916   }
917
918   // check case of quadratic faces
919   return InverseDiag(tr1,tr2);
920 }
921
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
925 //           fusion of triangles tr1 and tr2 having shared link on
926 //           theNode1 and theNode2
927 //=======================================================================
928
929 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
930                         const SMDS_MeshNode *    theNode1,
931                         const SMDS_MeshNode *    theNode2,
932                         const SMDS_MeshElement * tr1,
933                         const SMDS_MeshElement * tr2 )
934 {
935   if( tr1->NbNodes() != tr2->NbNodes() )
936     return false;
937   // find the 4-th node to insert into tr1
938   const SMDS_MeshNode* n4 = 0;
939   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
940   int i=0;
941   while ( !n4 && i<3 ) {
942     const SMDS_MeshNode * n = cast2Node( it->next() );
943     i++;
944     bool isDiag = ( n == theNode1 || n == theNode2 );
945     if ( !isDiag )
946       n4 = n;
947   }
948   // Make an array of nodes to be in a quadrangle
949   int iNode = 0, iFirstDiag = -1;
950   it = tr1->nodesIterator();
951   i=0;
952   while ( i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( isDiag ) {
957       if ( iFirstDiag < 0 )
958         iFirstDiag = iNode;
959       else if ( iNode - iFirstDiag == 1 )
960         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
961     }
962     else if ( n == n4 ) {
963       return false; // tr1 and tr2 should not have all the same nodes
964     }
965     theQuadNodes[ iNode++ ] = n;
966   }
967   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968     theQuadNodes[ iNode ] = n4;
969
970   return true;
971 }
972
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
976 //           with a quadrangle built on the same 4 nodes.
977 //           Return false if proper faces not found
978 //=======================================================================
979
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981                                    const SMDS_MeshNode * theNode2)
982 {
983   ClearLastCreated();
984
985   const SMDS_MeshElement *tr1, *tr2;
986   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
987     return false;
988
989   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
991     return false;
992
993   SMESHDS_Mesh * aMesh = GetMeshDS();
994
995   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996       (tr2->GetEntityType() == SMDSEntity_Triangle))
997   {
998     const SMDS_MeshNode* aNodes [ 4 ];
999     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1000       return false;
1001
1002     const SMDS_MeshElement* newElem = 0;
1003     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004     myLastCreatedElems.push_back(newElem);
1005     AddToSameGroups( newElem, tr1, aMesh );
1006     int aShapeId = tr1->getshapeId();
1007     if ( aShapeId )
1008       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009
1010     aMesh->RemoveElement( tr1 );
1011     aMesh->RemoveElement( tr2 );
1012
1013     return true;
1014   }
1015
1016   // check case of quadratic faces
1017   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1018     return false;
1019   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1020     return false;
1021
1022   //       5
1023   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1025   //    |   / |
1026   //  7 +  +  + 6
1027   //    | /9  |
1028   //    |/    |
1029   //  4 +--+--+ 3
1030   //       8
1031
1032   vector< const SMDS_MeshNode* > N1;
1033   vector< const SMDS_MeshNode* > N2;
1034   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1035     return false;
1036   // now we receive following N1 and N2 (using numeration as above image)
1037   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1038   // i.e. first nodes from both arrays determ new diagonal
1039
1040   const SMDS_MeshNode* aNodes[8];
1041   aNodes[0] = N1[0];
1042   aNodes[1] = N1[1];
1043   aNodes[2] = N2[0];
1044   aNodes[3] = N2[1];
1045   aNodes[4] = N1[3];
1046   aNodes[5] = N2[5];
1047   aNodes[6] = N2[3];
1048   aNodes[7] = N1[5];
1049
1050   const SMDS_MeshElement* newElem = 0;
1051   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053   myLastCreatedElems.push_back(newElem);
1054   AddToSameGroups( newElem, tr1, aMesh );
1055   int aShapeId = tr1->getshapeId();
1056   if ( aShapeId )
1057   {
1058     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1059   }
1060   aMesh->RemoveElement( tr1 );
1061   aMesh->RemoveElement( tr2 );
1062
1063   // remove middle node (9)
1064   GetMeshDS()->RemoveNode( N1[4] );
1065
1066   return true;
1067 }
1068
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose  : Reverse theElement orientation
1072 //=======================================================================
1073
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1075 {
1076   ClearLastCreated();
1077
1078   if (!theElem)
1079     return false;
1080   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081   if ( !it || !it->more() )
1082     return false;
1083
1084   const SMDSAbs_ElementType type = theElem->GetType();
1085   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1086     return false;
1087
1088   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1090   {
1091     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1092     if (!aPolyedre) {
1093       MESSAGE("Warning: bad volumic element");
1094       return false;
1095     }
1096     const int nbFaces = aPolyedre->NbFaces();
1097     vector<const SMDS_MeshNode *> poly_nodes;
1098     vector<int> quantities (nbFaces);
1099
1100     // reverse each face of the polyedre
1101     for (int iface = 1; iface <= nbFaces; iface++) {
1102       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1103       quantities[iface - 1] = nbFaceNodes;
1104
1105       for (inode = nbFaceNodes; inode >= 1; inode--) {
1106         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1107         poly_nodes.push_back(curNode);
1108       }
1109     }
1110     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1111   }
1112   else // other elements
1113   {
1114     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1115     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1116     if ( interlace.empty() )
1117     {
1118       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1119     }
1120     else
1121     {
1122       SMDS_MeshCell::applyInterlace( interlace, nodes );
1123     }
1124     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1125   }
1126   return false;
1127 }
1128
1129 //================================================================================
1130 /*!
1131  * \brief Reorient faces.
1132  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1133  * \param theDirection - desired direction of normal of \a theFace
1134  * \param theFace - one of \a theFaces that should be oriented according to
1135  *        \a theDirection and whose orientation defines orientation of other faces
1136  * \return number of reoriented faces.
1137  */
1138 //================================================================================
1139
1140 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1141                                   const gp_Dir&            theDirection,
1142                                   const SMDS_MeshElement * theFace)
1143 {
1144   int nbReori = 0;
1145   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1146
1147   if ( theFaces.empty() )
1148   {
1149     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1150     while ( fIt->more() )
1151       theFaces.insert( theFaces.end(), fIt->next() );
1152   }
1153
1154   // orient theFace according to theDirection
1155   gp_XYZ normal;
1156   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1157   if ( normal * theDirection.XYZ() < 0 )
1158     nbReori += Reorient( theFace );
1159
1160   // Orient other faces
1161
1162   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1163   TIDSortedElemSet avoidSet;
1164   set< SMESH_TLink > checkedLinks;
1165   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1166
1167   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1168     theFaces.erase( theFace );
1169   startFaces.insert( theFace );
1170
1171   int nodeInd1, nodeInd2;
1172   const SMDS_MeshElement*           otherFace;
1173   vector< const SMDS_MeshElement* > facesNearLink;
1174   vector< std::pair< int, int > >   nodeIndsOfFace;
1175
1176   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1177   while ( !startFaces.empty() )
1178   {
1179     startFace = startFaces.begin();
1180     theFace = *startFace;
1181     startFaces.erase( startFace );
1182     if ( !visitedFaces.insert( theFace ).second )
1183       continue;
1184
1185     avoidSet.clear();
1186     avoidSet.insert(theFace);
1187
1188     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1189
1190     const int nbNodes = theFace->NbCornerNodes();
1191     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1192     {
1193       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1194       linkIt_isNew = checkedLinks.insert( link );
1195       if ( !linkIt_isNew.second )
1196       {
1197         // link has already been checked and won't be encountered more
1198         // if the group (theFaces) is manifold
1199         //checkedLinks.erase( linkIt_isNew.first );
1200       }
1201       else
1202       {
1203         facesNearLink.clear();
1204         nodeIndsOfFace.clear();
1205         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1206                                                              theFaces, avoidSet,
1207                                                              &nodeInd1, &nodeInd2 )))
1208           if ( otherFace != theFace)
1209           {
1210             facesNearLink.push_back( otherFace );
1211             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1212             avoidSet.insert( otherFace );
1213           }
1214         if ( facesNearLink.size() > 1 )
1215         {
1216           // NON-MANIFOLD mesh shell !
1217           // select a face most co-directed with theFace,
1218           // other faces won't be visited this time
1219           gp_XYZ NF, NOF;
1220           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1221           double proj, maxProj = -1;
1222           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1223             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1224             if (( proj = Abs( NF * NOF )) > maxProj ) {
1225               maxProj = proj;
1226               otherFace = facesNearLink[i];
1227               nodeInd1  = nodeIndsOfFace[i].first;
1228               nodeInd2  = nodeIndsOfFace[i].second;
1229             }
1230           }
1231           // not to visit rejected faces
1232           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1233             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1234               visitedFaces.insert( facesNearLink[i] );
1235         }
1236         else if ( facesNearLink.size() == 1 )
1237         {
1238           otherFace = facesNearLink[0];
1239           nodeInd1  = nodeIndsOfFace.back().first;
1240           nodeInd2  = nodeIndsOfFace.back().second;
1241         }
1242         if ( otherFace && otherFace != theFace)
1243         {
1244           // link must be reverse in otherFace if orientation to otherFace
1245           // is same as that of theFace
1246           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1247           {
1248             nbReori += Reorient( otherFace );
1249           }
1250           startFaces.insert( otherFace );
1251         }
1252       }
1253       std::swap( link.first, link.second ); // reverse the link
1254     }
1255   }
1256   return nbReori;
1257 }
1258
1259 //================================================================================
1260 /*!
1261  * \brief Reorient faces basing on orientation of adjacent volumes.
1262  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1263  * \param theVolumes - reference volumes.
1264  * \param theOutsideNormal - to orient faces to have their normal
1265  *        pointing either \a outside or \a inside the adjacent volumes.
1266  * \return number of reoriented faces.
1267  */
1268 //================================================================================
1269
1270 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1271                                       TIDSortedElemSet & theVolumes,
1272                                       const bool         theOutsideNormal)
1273 {
1274   int nbReori = 0;
1275
1276   SMDS_ElemIteratorPtr faceIt;
1277   if ( theFaces.empty() )
1278     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1279   else
1280     faceIt = SMESHUtils::elemSetIterator( theFaces );
1281
1282   vector< const SMDS_MeshNode* > faceNodes;
1283   TIDSortedElemSet checkedVolumes;
1284   set< const SMDS_MeshNode* > faceNodesSet;
1285   SMDS_VolumeTool volumeTool;
1286
1287   while ( faceIt->more() ) // loop on given faces
1288   {
1289     const SMDS_MeshElement* face = faceIt->next();
1290     if ( face->GetType() != SMDSAbs_Face )
1291       continue;
1292
1293     const size_t nbCornersNodes = face->NbCornerNodes();
1294     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1295
1296     checkedVolumes.clear();
1297     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1298     while ( vIt->more() )
1299     {
1300       const SMDS_MeshElement* volume = vIt->next();
1301
1302       if ( !checkedVolumes.insert( volume ).second )
1303         continue;
1304       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1305         continue;
1306
1307       // is volume adjacent?
1308       bool allNodesCommon = true;
1309       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1310         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1311       if ( !allNodesCommon )
1312         continue;
1313
1314       // get nodes of a corresponding volume facet
1315       faceNodesSet.clear();
1316       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1317       volumeTool.Set( volume );
1318       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1319       if ( facetID < 0 ) continue;
1320       volumeTool.SetExternalNormal();
1321       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1322
1323       // compare order of faceNodes and facetNodes
1324       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1325       int iNN[2];
1326       for ( int i = 0; i < 2; ++i )
1327       {
1328         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1329         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1330           if ( faceNodes[ iN ] == n )
1331           {
1332             iNN[ i ] = iN;
1333             break;
1334           }
1335       }
1336       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1337       if ( isOutside != theOutsideNormal )
1338         nbReori += Reorient( face );
1339     }
1340   }  // loop on given faces
1341
1342   return nbReori;
1343 }
1344
1345 //=======================================================================
1346 //function : getBadRate
1347 //purpose  :
1348 //=======================================================================
1349
1350 static double getBadRate (const SMDS_MeshElement*               theElem,
1351                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1352 {
1353   SMESH::Controls::TSequenceOfXYZ P;
1354   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1355     return 1e100;
1356   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1357   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1358 }
1359
1360 //=======================================================================
1361 //function : QuadToTri
1362 //purpose  : Cut quadrangles into triangles.
1363 //           theCrit is used to select a diagonal to cut
1364 //=======================================================================
1365
1366 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1367                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1368 {
1369   ClearLastCreated();
1370
1371   if ( !theCrit.get() )
1372     return false;
1373
1374   SMESHDS_Mesh *       aMesh = GetMeshDS();
1375   Handle(Geom_Surface) surface;
1376   SMESH_MesherHelper   helper( *GetMesh() );
1377
1378   myLastCreatedElems.reserve( theElems.size() * 2 );
1379
1380   TIDSortedElemSet::iterator itElem;
1381   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1382   {
1383     const SMDS_MeshElement* elem = *itElem;
1384     if ( !elem || elem->GetType() != SMDSAbs_Face )
1385       continue;
1386     if ( elem->NbCornerNodes() != 4 )
1387       continue;
1388
1389     // retrieve element nodes
1390     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1391
1392     // compare two sets of possible triangles
1393     double aBadRate1, aBadRate2; // to what extent a set is bad
1394     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1395     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1396     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1397
1398     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1399     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1400     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1401
1402     const int aShapeId = FindShape( elem );
1403     const SMDS_MeshElement* newElem1 = 0;
1404     const SMDS_MeshElement* newElem2 = 0;
1405
1406     if ( !elem->IsQuadratic() ) // split linear quadrangle
1407     {
1408       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1409       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1410       if ( aBadRate1 <= aBadRate2 ) {
1411         // tr1 + tr2 is better
1412         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1413         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1414       }
1415       else {
1416         // tr3 + tr4 is better
1417         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1418         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1419       }
1420     }
1421     else // split quadratic quadrangle
1422     {
1423       helper.SetIsQuadratic( true );
1424       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1425
1426       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1427       if ( aNodes.size() == 9 )
1428       {
1429         helper.SetIsBiQuadratic( true );
1430         if ( aBadRate1 <= aBadRate2 )
1431           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1432         else
1433           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1434       }
1435       // create a new element
1436       if ( aBadRate1 <= aBadRate2 ) {
1437         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1438         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1439       }
1440       else {
1441         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1442         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1443       }
1444     } // quadratic case
1445
1446     // care of a new element
1447
1448     myLastCreatedElems.push_back(newElem1);
1449     myLastCreatedElems.push_back(newElem2);
1450     AddToSameGroups( newElem1, elem, aMesh );
1451     AddToSameGroups( newElem2, elem, aMesh );
1452
1453     // put a new triangle on the same shape
1454     if ( aShapeId )
1455       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1456     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1457
1458     aMesh->RemoveElement( elem );
1459   }
1460   return true;
1461 }
1462
1463 //=======================================================================
1464 /*!
1465  * \brief Split each of given quadrangles into 4 triangles.
1466  * \param theElems - The faces to be split. If empty all faces are split.
1467  */
1468 //=======================================================================
1469
1470 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1471 {
1472   ClearLastCreated();
1473   myLastCreatedElems.reserve( theElems.size() * 4 );
1474
1475   SMESH_MesherHelper helper( *GetMesh() );
1476   helper.SetElementsOnShape( true );
1477
1478   SMDS_ElemIteratorPtr faceIt;
1479   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1480   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1481
1482   bool   checkUV;
1483   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1484   gp_XYZ xyz[9];
1485   vector< const SMDS_MeshNode* > nodes;
1486   SMESHDS_SubMesh*               subMeshDS = 0;
1487   TopoDS_Face                    F;
1488   Handle(Geom_Surface)           surface;
1489   TopLoc_Location                loc;
1490
1491   while ( faceIt->more() )
1492   {
1493     const SMDS_MeshElement* quad = faceIt->next();
1494     if ( !quad || quad->NbCornerNodes() != 4 )
1495       continue;
1496
1497     // get a surface the quad is on
1498
1499     if ( quad->getshapeId() < 1 )
1500     {
1501       F.Nullify();
1502       helper.SetSubShape( 0 );
1503       subMeshDS = 0;
1504     }
1505     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1506     {
1507       helper.SetSubShape( quad->getshapeId() );
1508       if ( !helper.GetSubShape().IsNull() &&
1509            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1510       {
1511         F = TopoDS::Face( helper.GetSubShape() );
1512         surface = BRep_Tool::Surface( F, loc );
1513         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1514       }
1515       else
1516       {
1517         helper.SetSubShape( 0 );
1518         subMeshDS = 0;
1519       }
1520     }
1521
1522     // create a central node
1523
1524     const SMDS_MeshNode* nCentral;
1525     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1526
1527     if ( nodes.size() == 9 )
1528     {
1529       nCentral = nodes.back();
1530     }
1531     else
1532     {
1533       size_t iN = 0;
1534       if ( F.IsNull() )
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1538
1539         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1540           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1541
1542         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                    xyz[0], xyz[1], xyz[2], xyz[3],
1544                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1545       }
1546       else
1547       {
1548         for ( ; iN < nodes.size(); ++iN )
1549           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1550
1551         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1552           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1553
1554         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1555                                   uv[0], uv[1], uv[2], uv[3],
1556                                   uv[4], uv[5], uv[6], uv[7] );
1557
1558         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1559         xyz[ 8 ] = p.XYZ();
1560       }
1561
1562       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1563                                  uv[8].X(), uv[8].Y() );
1564       myLastCreatedNodes.push_back( nCentral );
1565     }
1566
1567     // create 4 triangles
1568
1569     helper.SetIsQuadratic  ( nodes.size() > 4 );
1570     helper.SetIsBiQuadratic( nodes.size() == 9 );
1571     if ( helper.GetIsQuadratic() )
1572       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1573
1574     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1575
1576     for ( int i = 0; i < 4; ++i )
1577     {
1578       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1579                                                nodes[(i+1)%4],
1580                                                nCentral );
1581       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1582       myLastCreatedElems.push_back( tria );
1583     }
1584   }
1585 }
1586
1587 //=======================================================================
1588 //function : BestSplit
1589 //purpose  : Find better diagonal for cutting.
1590 //=======================================================================
1591
1592 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1593                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1594 {
1595   ClearLastCreated();
1596
1597   if (!theCrit.get())
1598     return -1;
1599
1600   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1601     return -1;
1602
1603   if( theQuad->NbNodes()==4 ||
1604       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1605
1606     // retrieve element nodes
1607     const SMDS_MeshNode* aNodes [4];
1608     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1609     int i = 0;
1610     //while (itN->more())
1611     while (i<4) {
1612       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1613     }
1614     // compare two sets of possible triangles
1615     double aBadRate1, aBadRate2; // to what extent a set is bad
1616     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1617     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1618     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1619
1620     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1621     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1622     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1623     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1624     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1625     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1626       return 1; // diagonal 1-3
1627
1628     return 2; // diagonal 2-4
1629   }
1630   return -1;
1631 }
1632
1633 namespace
1634 {
1635   // Methods of splitting volumes into tetra
1636
1637   const int theHexTo5_1[5*4+1] =
1638     {
1639       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1640     };
1641   const int theHexTo5_2[5*4+1] =
1642     {
1643       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1644     };
1645   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1646
1647   const int theHexTo6_1[6*4+1] =
1648     {
1649       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1650     };
1651   const int theHexTo6_2[6*4+1] =
1652     {
1653       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1654     };
1655   const int theHexTo6_3[6*4+1] =
1656     {
1657       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1658     };
1659   const int theHexTo6_4[6*4+1] =
1660     {
1661       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1662     };
1663   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1664
1665   const int thePyraTo2_1[2*4+1] =
1666     {
1667       0, 1, 2, 4,    0, 2, 3, 4,   -1
1668     };
1669   const int thePyraTo2_2[2*4+1] =
1670     {
1671       1, 2, 3, 4,    1, 3, 0, 4,   -1
1672     };
1673   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1674
1675   const int thePentaTo3_1[3*4+1] =
1676     {
1677       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1678     };
1679   const int thePentaTo3_2[3*4+1] =
1680     {
1681       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1682     };
1683   const int thePentaTo3_3[3*4+1] =
1684     {
1685       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1686     };
1687   const int thePentaTo3_4[3*4+1] =
1688     {
1689       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1690     };
1691   const int thePentaTo3_5[3*4+1] =
1692     {
1693       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1694     };
1695   const int thePentaTo3_6[3*4+1] =
1696     {
1697       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1698     };
1699   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1700                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1701
1702   // Methods of splitting hexahedron into prisms
1703
1704   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1705     {
1706       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1707     };
1708   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1709     {
1710       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1711     };
1712   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1713     {
1714       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1715     };
1716
1717   const int theHexTo2Prisms_BT_1[6*2+1] =
1718     {
1719       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1720     };
1721   const int theHexTo2Prisms_BT_2[6*2+1] =
1722     {
1723       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1724     };
1725   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1726
1727   const int theHexTo2Prisms_LR_1[6*2+1] =
1728     {
1729       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1730     };
1731   const int theHexTo2Prisms_LR_2[6*2+1] =
1732     {
1733       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1734     };
1735   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1736
1737   const int theHexTo2Prisms_FB_1[6*2+1] =
1738     {
1739       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1740     };
1741   const int theHexTo2Prisms_FB_2[6*2+1] =
1742     {
1743       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1744     };
1745   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1746
1747
1748   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1749   {
1750     int _n1, _n2, _n3;
1751     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1752     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1753     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1754                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1755   };
1756   struct TSplitMethod
1757   {
1758     int        _nbSplits;
1759     int        _nbCorners;
1760     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1761     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1762     bool       _ownConn;      //!< to delete _connectivity in destructor
1763     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1764
1765     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1766       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1767     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1768     bool hasFacet( const TTriangleFacet& facet ) const
1769     {
1770       if ( _nbCorners == 4 )
1771       {
1772         const int* tetConn = _connectivity;
1773         for ( ; tetConn[0] >= 0; tetConn += 4 )
1774           if (( facet.contains( tetConn[0] ) +
1775                 facet.contains( tetConn[1] ) +
1776                 facet.contains( tetConn[2] ) +
1777                 facet.contains( tetConn[3] )) == 3 )
1778             return true;
1779       }
1780       else // prism, _nbCorners == 6
1781       {
1782         const int* prismConn = _connectivity;
1783         for ( ; prismConn[0] >= 0; prismConn += 6 )
1784         {
1785           if (( facet.contains( prismConn[0] ) &&
1786                 facet.contains( prismConn[1] ) &&
1787                 facet.contains( prismConn[2] ))
1788               ||
1789               ( facet.contains( prismConn[3] ) &&
1790                 facet.contains( prismConn[4] ) &&
1791                 facet.contains( prismConn[5] )))
1792             return true;
1793         }
1794       }
1795       return false;
1796     }
1797   };
1798
1799   //=======================================================================
1800   /*!
1801    * \brief return TSplitMethod for the given element to split into tetrahedra
1802    */
1803   //=======================================================================
1804
1805   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1806   {
1807     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1808
1809     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1810     // an edge and a face barycenter; tertaherdons are based on triangles and
1811     // a volume barycenter
1812     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1813
1814     // Find out how adjacent volumes are split
1815
1816     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1817     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1818     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1819     {
1820       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1821       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1822       if ( nbNodes < 4 ) continue;
1823
1824       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1825       const int* nInd = vol.GetFaceNodesIndices( iF );
1826       if ( nbNodes == 4 )
1827       {
1828         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1829         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1830         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1831         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1832       }
1833       else
1834       {
1835         int iCom = 0; // common node of triangle faces to split into
1836         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1837         {
1838           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1839                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1840                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1841           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1842                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1843                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1844           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1845           {
1846             triaSplits.push_back( t012 );
1847             triaSplits.push_back( t023 );
1848             break;
1849           }
1850         }
1851       }
1852       if ( !triaSplits.empty() )
1853         hasAdjacentSplits = true;
1854     }
1855
1856     // Among variants of split method select one compliant with adjacent volumes
1857
1858     TSplitMethod method;
1859     if ( !vol.Element()->IsPoly() && !is24TetMode )
1860     {
1861       int nbVariants = 2, nbTet = 0;
1862       const int** connVariants = 0;
1863       switch ( vol.Element()->GetEntityType() )
1864       {
1865       case SMDSEntity_Hexa:
1866       case SMDSEntity_Quad_Hexa:
1867       case SMDSEntity_TriQuad_Hexa:
1868         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1869           connVariants = theHexTo5, nbTet = 5;
1870         else
1871           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1872         break;
1873       case SMDSEntity_Pyramid:
1874       case SMDSEntity_Quad_Pyramid:
1875         connVariants = thePyraTo2;  nbTet = 2;
1876         break;
1877       case SMDSEntity_Penta:
1878       case SMDSEntity_Quad_Penta:
1879       case SMDSEntity_BiQuad_Penta:
1880         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1881         break;
1882       default:
1883         nbVariants = 0;
1884       }
1885       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1886       {
1887         // check method compliancy with adjacent tetras,
1888         // all found splits must be among facets of tetras described by this method
1889         method = TSplitMethod( nbTet, connVariants[variant] );
1890         if ( hasAdjacentSplits && method._nbSplits > 0 )
1891         {
1892           bool facetCreated = true;
1893           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1894           {
1895             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1896             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1897               facetCreated = method.hasFacet( *facet );
1898           }
1899           if ( !facetCreated )
1900             method = TSplitMethod(0); // incompatible method
1901         }
1902       }
1903     }
1904     if ( method._nbSplits < 1 )
1905     {
1906       // No standard method is applicable, use a generic solution:
1907       // each facet of a volume is split into triangles and
1908       // each of triangles and a volume barycenter form a tetrahedron.
1909
1910       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1911
1912       int* connectivity = new int[ maxTetConnSize + 1 ];
1913       method._connectivity = connectivity;
1914       method._ownConn = true;
1915       method._baryNode = !isHex27; // to create central node or not
1916
1917       int connSize = 0;
1918       int baryCenInd = vol.NbNodes() - int( isHex27 );
1919       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1920       {
1921         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1922         const int*   nInd = vol.GetFaceNodesIndices( iF );
1923         // find common node of triangle facets of tetra to create
1924         int iCommon = 0; // index in linear numeration
1925         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1926         if ( !triaSplits.empty() )
1927         {
1928           // by found facets
1929           const TTriangleFacet* facet = &triaSplits.front();
1930           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1931             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1932                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1933               break;
1934         }
1935         else if ( nbNodes > 3 && !is24TetMode )
1936         {
1937           // find the best method of splitting into triangles by aspect ratio
1938           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1939           map< double, int > badness2iCommon;
1940           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1941           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1942           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1943           {
1944             double badness = 0;
1945             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1946             {
1947               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1948                                       nodes[ iQ*((iLast-1)%nbNodes)],
1949                                       nodes[ iQ*((iLast  )%nbNodes)]);
1950               badness += getBadRate( &tria, aspectRatio );
1951             }
1952             badness2iCommon.insert( make_pair( badness, iCommon ));
1953           }
1954           // use iCommon with lowest badness
1955           iCommon = badness2iCommon.begin()->second;
1956         }
1957         if ( iCommon >= nbNodes )
1958           iCommon = 0; // something wrong
1959
1960         // fill connectivity of tetrahedra based on a current face
1961         int nbTet = nbNodes - 2;
1962         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1963         {
1964           int faceBaryCenInd;
1965           if ( isHex27 )
1966           {
1967             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1968             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1969           }
1970           else
1971           {
1972             method._faceBaryNode[ iF ] = 0;
1973             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1974           }
1975           nbTet = nbNodes;
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = i, i2 = (i+1) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982             connectivity[ connSize++ ] = faceBaryCenInd;
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         else
1987         {
1988           for ( int i = 0; i < nbTet; ++i )
1989           {
1990             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1991             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1992             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1993             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1994             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1995             connectivity[ connSize++ ] = baryCenInd;
1996           }
1997         }
1998         method._nbSplits += nbTet;
1999
2000       } // loop on volume faces
2001
2002       connectivity[ connSize++ ] = -1;
2003
2004     } // end of generic solution
2005
2006     return method;
2007   }
2008   //=======================================================================
2009   /*!
2010    * \brief return TSplitMethod to split haxhedron into prisms
2011    */
2012   //=======================================================================
2013
2014   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2015                                     const int        methodFlags,
2016                                     const int        facetToSplit)
2017   {
2018     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2019     // B, T, L, B, R, F
2020     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2021
2022     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2023     {
2024       static TSplitMethod to4methods[4]; // order BT, LR, FB
2025       if ( to4methods[iF]._nbSplits == 0 )
2026       {
2027         switch ( iF ) {
2028         case 0:
2029           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2030           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2031           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2032           break;
2033         case 1:
2034           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2035           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2036           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2037           break;
2038         case 2:
2039           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2040           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2041           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2042           break;
2043         default: return to4methods[3];
2044         }
2045         to4methods[iF]._nbSplits  = 4;
2046         to4methods[iF]._nbCorners = 6;
2047       }
2048       return to4methods[iF];
2049     }
2050     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2051
2052     TSplitMethod method;
2053
2054     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2055
2056     const int nbVariants = 2, nbSplits = 2;
2057     const int** connVariants = 0;
2058     switch ( iF ) {
2059     case 0: connVariants = theHexTo2Prisms_BT; break;
2060     case 1: connVariants = theHexTo2Prisms_LR; break;
2061     case 2: connVariants = theHexTo2Prisms_FB; break;
2062     default: return method;
2063     }
2064
2065     // look for prisms adjacent via facetToSplit and an opposite one
2066     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2067     {
2068       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2069       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2070       if ( nbNodes != 4 ) return method;
2071
2072       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2073       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2074       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2075       TTriangleFacet* t;
2076       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2077         t = &t012;
2078       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2079         t = &t123;
2080       else
2081         continue;
2082
2083       // there are adjacent prism
2084       for ( int variant = 0; variant < nbVariants; ++variant )
2085       {
2086         // check method compliancy with adjacent prisms,
2087         // the found prism facets must be among facets of prisms described by current method
2088         method._nbSplits     = nbSplits;
2089         method._nbCorners    = 6;
2090         method._connectivity = connVariants[ variant ];
2091         if ( method.hasFacet( *t ))
2092           return method;
2093       }
2094     }
2095
2096     // No adjacent prisms. Select a variant with a best aspect ratio.
2097
2098     double badness[2] = { 0., 0. };
2099     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2100     const SMDS_MeshNode** nodes = vol.GetNodes();
2101     for ( int variant = 0; variant < nbVariants; ++variant )
2102       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103       {
2104         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2106
2107         method._connectivity = connVariants[ variant ];
2108         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2111
2112         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2113                                 nodes[ t->_n2 ],
2114                                 nodes[ t->_n3 ] );
2115         badness[ variant ] += getBadRate( &tria, aspectRatio );
2116       }
2117     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2118
2119     method._nbSplits     = nbSplits;
2120     method._nbCorners    = 6;
2121     method._connectivity = connVariants[ iBetter ];
2122
2123     return method;
2124   }
2125
2126   //================================================================================
2127   /*!
2128    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2129    */
2130   //================================================================================
2131
2132   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2133                                        const SMDSAbs_GeometryType geom ) const
2134   {
2135     // find the tetrahedron including the three nodes of facet
2136     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2137     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2138     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2139     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2140     while ( volIt1->more() )
2141     {
2142       const SMDS_MeshElement* v = volIt1->next();
2143       if ( v->GetGeomType() != geom )
2144         continue;
2145       const int lastCornerInd = v->NbCornerNodes() - 1;
2146       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2147         continue; // medium node not allowed
2148       const int ind2 = v->GetNodeIndex( n2 );
2149       if ( ind2 < 0 || lastCornerInd < ind2 )
2150         continue;
2151       const int ind3 = v->GetNodeIndex( n3 );
2152       if ( ind3 < 0 || lastCornerInd < ind3 )
2153         continue;
2154       return true;
2155     }
2156     return false;
2157   }
2158
2159   //=======================================================================
2160   /*!
2161    * \brief A key of a face of volume
2162    */
2163   //=======================================================================
2164
2165   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2166   {
2167     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2168     {
2169       TIDSortedNodeSet sortedNodes;
2170       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2171       int nbNodes = vol.NbFaceNodes( iF );
2172       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2173       for ( int i = 0; i < nbNodes; i += iQ )
2174         sortedNodes.insert( fNodes[i] );
2175       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2176       first.first   = (*(n++))->GetID();
2177       first.second  = (*(n++))->GetID();
2178       second.first  = (*(n++))->GetID();
2179       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2180     }
2181   };
2182 } // namespace
2183
2184 //=======================================================================
2185 //function : SplitVolumes
2186 //purpose  : Split volume elements into tetrahedra or prisms.
2187 //           If facet ID < 0, element is split into tetrahedra,
2188 //           else a hexahedron is split into prisms so that the given facet is
2189 //           split into triangles
2190 //=======================================================================
2191
2192 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2193                                      const int            theMethodFlags)
2194 {
2195   SMDS_VolumeTool    volTool;
2196   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2197   fHelper.ToFixNodeParameters( true );
2198
2199   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2200   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2201
2202   SMESH_SequenceOfElemPtr newNodes, newElems;
2203
2204   // map face of volume to it's baricenrtic node
2205   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2206   double bc[3];
2207   vector<const SMDS_MeshElement* > splitVols;
2208
2209   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2210   for ( ; elem2facet != theElems.end(); ++elem2facet )
2211   {
2212     const SMDS_MeshElement* elem = elem2facet->first;
2213     const int       facetToSplit = elem2facet->second;
2214     if ( elem->GetType() != SMDSAbs_Volume )
2215       continue;
2216     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2217     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2218       continue;
2219
2220     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2221
2222     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2223                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2224                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2225     if ( splitMethod._nbSplits < 1 ) continue;
2226
2227     // find submesh to add new tetras to
2228     if ( !subMesh || !subMesh->Contains( elem ))
2229     {
2230       int shapeID = FindShape( elem );
2231       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2232       subMesh = GetMeshDS()->MeshElements( shapeID );
2233     }
2234     int iQ;
2235     if ( elem->IsQuadratic() )
2236     {
2237       iQ = 2;
2238       // add quadratic links to the helper
2239       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2240       {
2241         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2242         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2243         for ( int iN = 0; iN < nbN; iN += iQ )
2244           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2245       }
2246       helper.SetIsQuadratic( true );
2247     }
2248     else
2249     {
2250       iQ = 1;
2251       helper.SetIsQuadratic( false );
2252     }
2253     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2254                                         volTool.GetNodes() + elem->NbNodes() );
2255     helper.SetElementsOnShape( true );
2256     if ( splitMethod._baryNode )
2257     {
2258       // make a node at barycenter
2259       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2260       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2261       nodes.push_back( gcNode );
2262       newNodes.push_back( gcNode );
2263     }
2264     if ( !splitMethod._faceBaryNode.empty() )
2265     {
2266       // make or find baricentric nodes of faces
2267       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2268       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2269       {
2270         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2271           volFace2BaryNode.insert
2272           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2273         if ( !f_n->second )
2274         {
2275           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2276           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2277         }
2278         nodes.push_back( iF_n->second = f_n->second );
2279       }
2280     }
2281
2282     // make new volumes
2283     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2284     const int* volConn = splitMethod._connectivity;
2285     if ( splitMethod._nbCorners == 4 ) // tetra
2286       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2287         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2288                                                                nodes[ volConn[1] ],
2289                                                                nodes[ volConn[2] ],
2290                                                                nodes[ volConn[3] ]));
2291     else // prisms
2292       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2293         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2294                                                                nodes[ volConn[1] ],
2295                                                                nodes[ volConn[2] ],
2296                                                                nodes[ volConn[3] ],
2297                                                                nodes[ volConn[4] ],
2298                                                                nodes[ volConn[5] ]));
2299
2300     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2301
2302     // Split faces on sides of the split volume
2303
2304     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2305     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2306     {
2307       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2308       if ( nbNodes < 4 ) continue;
2309
2310       // find an existing face
2311       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2312                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2313       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2314                                                                        /*noMedium=*/false))
2315       {
2316         // make triangles
2317         helper.SetElementsOnShape( false );
2318         vector< const SMDS_MeshElement* > triangles;
2319
2320         // find submesh to add new triangles in
2321         if ( !fSubMesh || !fSubMesh->Contains( face ))
2322         {
2323           int shapeID = FindShape( face );
2324           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2325         }
2326         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2327         if ( iF_n != splitMethod._faceBaryNode.end() )
2328         {
2329           const SMDS_MeshNode *baryNode = iF_n->second;
2330           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2331           {
2332             const SMDS_MeshNode* n1 = fNodes[iN];
2333             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2334             const SMDS_MeshNode *n3 = baryNode;
2335             if ( !volTool.IsFaceExternal( iF ))
2336               swap( n2, n3 );
2337             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2338           }
2339           if ( fSubMesh ) // update position of the bary node on geometry
2340           {
2341             if ( subMesh )
2342               subMesh->RemoveNode( baryNode );
2343             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2344             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2345             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2346             {
2347               fHelper.SetSubShape( s );
2348               gp_XY uv( 1e100, 1e100 );
2349               double distXYZ[4];
2350               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2351                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2352                    uv.X() < 1e100 )
2353               {
2354                 // node is too far from the surface
2355                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2356                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2357                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2358               }
2359             }
2360           }
2361         }
2362         else
2363         {
2364           // among possible triangles create ones described by split method
2365           const int* nInd = volTool.GetFaceNodesIndices( iF );
2366           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2367           int iCom = 0; // common node of triangle faces to split into
2368           list< TTriangleFacet > facets;
2369           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2370           {
2371             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2372                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2373                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2374             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2375                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2376                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2377             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2378             {
2379               facets.push_back( t012 );
2380               facets.push_back( t023 );
2381               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2382                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2383                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2384                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2385               break;
2386             }
2387           }
2388           list< TTriangleFacet >::iterator facet = facets.begin();
2389           if ( facet == facets.end() )
2390             break;
2391           for ( ; facet != facets.end(); ++facet )
2392           {
2393             if ( !volTool.IsFaceExternal( iF ))
2394               swap( facet->_n2, facet->_n3 );
2395             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2396                                                  volNodes[ facet->_n2 ],
2397                                                  volNodes[ facet->_n3 ]));
2398           }
2399         }
2400         for ( size_t i = 0; i < triangles.size(); ++i )
2401         {
2402           if ( !triangles[ i ]) continue;
2403           if ( fSubMesh )
2404             fSubMesh->AddElement( triangles[ i ]);
2405           newElems.push_back( triangles[ i ]);
2406         }
2407         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2408         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2409
2410       } // while a face based on facet nodes exists
2411     } // loop on volume faces to split them into triangles
2412
2413     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2414
2415     if ( geomType == SMDSEntity_TriQuad_Hexa )
2416     {
2417       // remove medium nodes that could become free
2418       for ( int i = 20; i < volTool.NbNodes(); ++i )
2419         if ( volNodes[i]->NbInverseElements() == 0 )
2420           GetMeshDS()->RemoveNode( volNodes[i] );
2421     }
2422   } // loop on volumes to split
2423
2424   myLastCreatedNodes = newNodes;
2425   myLastCreatedElems = newElems;
2426 }
2427
2428 //=======================================================================
2429 //function : GetHexaFacetsToSplit
2430 //purpose  : For hexahedra that will be split into prisms, finds facets to
2431 //           split into triangles. Only hexahedra adjacent to the one closest
2432 //           to theFacetNormal.Location() are returned.
2433 //param [in,out] theHexas - the hexahedra
2434 //param [in]     theFacetNormal - facet normal
2435 //param [out]    theFacets - the hexahedra and found facet IDs
2436 //=======================================================================
2437
2438 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2439                                              const gp_Ax1&     theFacetNormal,
2440                                              TFacetOfElem &    theFacets)
2441 {
2442 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2443
2444   // Find a hexa closest to the location of theFacetNormal
2445
2446   const SMDS_MeshElement* startHex;
2447   {
2448     // get SMDS_ElemIteratorPtr on theHexas
2449     typedef const SMDS_MeshElement*                                      TValue;
2450     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2451     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2452     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2453     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2454     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2455       ( new TElemSetIter( theHexas.begin(),
2456                           theHexas.end(),
2457                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2458
2459     SMESH_ElementSearcher* searcher =
2460       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2461
2462     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2463
2464     delete searcher;
2465
2466     if ( !startHex )
2467       throw SALOME_Exception( THIS_METHOD "startHex not found");
2468   }
2469
2470   // Select a facet of startHex by theFacetNormal
2471
2472   SMDS_VolumeTool vTool( startHex );
2473   double norm[3], dot, maxDot = 0;
2474   int facetID = -1;
2475   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2476     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2477     {
2478       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2479       if ( dot > maxDot )
2480       {
2481         facetID = iF;
2482         maxDot = dot;
2483       }
2484     }
2485   if ( facetID < 0 )
2486     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2487
2488   // Fill theFacets starting from facetID of startHex
2489
2490   // facets used for searching of volumes adjacent to already treated ones
2491   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2492   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2493   TFacetMap facetsToCheck;
2494
2495   set<const SMDS_MeshNode*> facetNodes;
2496   const SMDS_MeshElement*   curHex;
2497
2498   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2499
2500   while ( startHex )
2501   {
2502     // move in two directions from startHex via facetID
2503     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2504     {
2505       curHex       = startHex;
2506       int curFacet = facetID;
2507       if ( is2nd ) // do not treat startHex twice
2508       {
2509         vTool.Set( curHex );
2510         if ( vTool.IsFreeFace( curFacet, &curHex ))
2511         {
2512           curHex = 0;
2513         }
2514         else
2515         {
2516           vTool.GetFaceNodes( curFacet, facetNodes );
2517           vTool.Set( curHex );
2518           curFacet = vTool.GetFaceIndex( facetNodes );
2519         }
2520       }
2521       while ( curHex )
2522       {
2523         // store a facet to split
2524         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2525         {
2526           theFacets.insert( make_pair( curHex, -1 ));
2527           break;
2528         }
2529         if ( !allHex && !theHexas.count( curHex ))
2530           break;
2531
2532         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2533           theFacets.insert( make_pair( curHex, curFacet ));
2534         if ( !facetIt2isNew.second )
2535           break;
2536
2537         // remember not-to-split facets in facetsToCheck
2538         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2539         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2540         {
2541           if ( iF == curFacet && iF == oppFacet )
2542             continue;
2543           TVolumeFaceKey facetKey ( vTool, iF );
2544           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2545           pair< TFacetMap::iterator, bool > it2isnew =
2546             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2547           if ( !it2isnew.second )
2548             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2549         }
2550         // pass to a volume adjacent via oppFacet
2551         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2552         {
2553           curHex = 0;
2554         }
2555         else
2556         {
2557           // get a new curFacet
2558           vTool.GetFaceNodes( oppFacet, facetNodes );
2559           vTool.Set( curHex );
2560           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2561         }
2562       }
2563     } // move in two directions from startHex via facetID
2564
2565     // Find a new startHex by facetsToCheck
2566
2567     startHex = 0;
2568     facetID  = -1;
2569     TFacetMap::iterator fIt = facetsToCheck.begin();
2570     while ( !startHex && fIt != facetsToCheck.end() )
2571     {
2572       const TElemFacets&  elemFacets = fIt->second;
2573       const SMDS_MeshElement*    hex = elemFacets.first->first;
2574       int                 splitFacet = elemFacets.first->second;
2575       int               lateralFacet = elemFacets.second;
2576       facetsToCheck.erase( fIt );
2577       fIt = facetsToCheck.begin();
2578
2579       vTool.Set( hex );
2580       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2581            curHex->GetGeomType() != SMDSGeom_HEXA )
2582         continue;
2583       if ( !allHex && !theHexas.count( curHex ))
2584         continue;
2585
2586       startHex = curHex;
2587
2588       // find a facet of startHex to split
2589
2590       set<const SMDS_MeshNode*> lateralNodes;
2591       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2592       vTool.GetFaceNodes( splitFacet,   facetNodes );
2593       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2594       vTool.Set( startHex );
2595       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2596
2597       // look for a facet of startHex having common nodes with facetNodes
2598       // but not lateralFacet
2599       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2600       {
2601         if ( iF == lateralFacet )
2602           continue;
2603         int nbCommonNodes = 0;
2604         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2605         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2606           nbCommonNodes += facetNodes.count( nn[ iN ]);
2607
2608         if ( nbCommonNodes >= 2 )
2609         {
2610           facetID = iF;
2611           break;
2612         }
2613       }
2614       if ( facetID < 0 )
2615         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2616     }
2617   } //   while ( startHex )
2618
2619   return;
2620 }
2621
2622 namespace
2623 {
2624   //================================================================================
2625   /*!
2626    * \brief Selects nodes of several elements according to a given interlace
2627    *  \param [in] srcNodes - nodes to select from
2628    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2629    *  \param [in] interlace - indices of nodes for all elements
2630    *  \param [in] nbElems - nb of elements
2631    *  \param [in] nbNodes - nb of nodes in each element
2632    *  \param [in] mesh - the mesh
2633    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2634    *  \param [in] type - type of elements to look for
2635    */
2636   //================================================================================
2637
2638   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2639                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2640                     const int*                            interlace,
2641                     const int                             nbElems,
2642                     const int                             nbNodes,
2643                     SMESHDS_Mesh*                         mesh = 0,
2644                     list< const SMDS_MeshElement* >*      elemQueue=0,
2645                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2646   {
2647     for ( int iE = 0; iE < nbElems; ++iE )
2648     {
2649       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2650       const int*                         select = & interlace[iE*nbNodes];
2651       elemNodes.resize( nbNodes );
2652       for ( int iN = 0; iN < nbNodes; ++iN )
2653         elemNodes[iN] = srcNodes[ select[ iN ]];
2654     }
2655     const SMDS_MeshElement* e;
2656     if ( elemQueue )
2657       for ( int iE = 0; iE < nbElems; ++iE )
2658         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2659           elemQueue->push_back( e );
2660   }
2661 }
2662
2663 //=======================================================================
2664 /*
2665  * Split bi-quadratic elements into linear ones without creation of additional nodes
2666  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2667  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2668  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2669  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2670  *   will be split in order to keep the mesh conformal.
2671  *  \param elems - elements to split
2672  */
2673 //=======================================================================
2674
2675 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2676 {
2677   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2678   vector<const SMDS_MeshElement* > splitElems;
2679   list< const SMDS_MeshElement* > elemQueue;
2680   list< const SMDS_MeshElement* >::iterator elemIt;
2681
2682   SMESHDS_Mesh * mesh = GetMeshDS();
2683   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2684   int nbElems, nbNodes;
2685
2686   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2687   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2688   {
2689     elemQueue.clear();
2690     elemQueue.push_back( *elemSetIt );
2691     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2692     {
2693       const SMDS_MeshElement* elem = *elemIt;
2694       switch( elem->GetEntityType() )
2695       {
2696       case SMDSEntity_TriQuad_Hexa: // HEX27
2697       {
2698         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2699         nbElems  = nbNodes = 8;
2700         elemType = & hexaType;
2701
2702         // get nodes for new elements
2703         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2704                                  { 1,9,20,8,    17,22,26,21 },
2705                                  { 2,10,20,9,   18,23,26,22 },
2706                                  { 3,11,20,10,  19,24,26,23 },
2707                                  { 16,21,26,24, 4,12,25,15  },
2708                                  { 17,22,26,21, 5,13,25,12  },
2709                                  { 18,23,26,22, 6,14,25,13  },
2710                                  { 19,24,26,23, 7,15,25,14  }};
2711         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2712
2713         // add boundary faces to elemQueue
2714         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2715                                  { 4,5,6,7, 12,13,14,15, 25 },
2716                                  { 0,1,5,4, 8,17,12,16,  21 },
2717                                  { 1,2,6,5, 9,18,13,17,  22 },
2718                                  { 2,3,7,6, 10,19,14,18, 23 },
2719                                  { 3,0,4,7, 11,16,15,19, 24 }};
2720         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2721
2722         // add boundary segments to elemQueue
2723         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2724                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2725                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2726         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2727         break;
2728       }
2729       case SMDSEntity_BiQuad_Triangle: // TRIA7
2730       {
2731         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2732         nbElems = 3;
2733         nbNodes = 4;
2734         elemType = & quadType;
2735
2736         // get nodes for new elements
2737         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2738         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2739
2740         // add boundary segments to elemQueue
2741         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2742         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2743         break;
2744       }
2745       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2746       {
2747         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2748         nbElems = 4;
2749         nbNodes = 4;
2750         elemType = & quadType;
2751
2752         // get nodes for new elements
2753         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2754         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2755
2756         // add boundary segments to elemQueue
2757         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2758         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2759         break;
2760       }
2761       case SMDSEntity_Quad_Edge:
2762       {
2763         if ( elemIt == elemQueue.begin() )
2764           continue; // an elem is in theElems
2765         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2766         nbElems = 2;
2767         nbNodes = 2;
2768         elemType = & segType;
2769
2770         // get nodes for new elements
2771         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2772         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2773         break;
2774       }
2775       default: continue;
2776       } // switch( elem->GetEntityType() )
2777
2778       // Create new elements
2779
2780       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2781
2782       splitElems.clear();
2783
2784       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2785       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2786       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2787       //elemType->SetID( -1 );
2788
2789       for ( int iE = 0; iE < nbElems; ++iE )
2790         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2791
2792
2793       ReplaceElemInGroups( elem, splitElems, mesh );
2794
2795       if ( subMesh )
2796         for ( size_t i = 0; i < splitElems.size(); ++i )
2797           subMesh->AddElement( splitElems[i] );
2798     }
2799   }
2800 }
2801
2802 //=======================================================================
2803 //function : AddToSameGroups
2804 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2805 //=======================================================================
2806
2807 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2808                                         const SMDS_MeshElement* elemInGroups,
2809                                         SMESHDS_Mesh *          aMesh)
2810 {
2811   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2812   if (!groups.empty()) {
2813     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2814     for ( ; grIt != groups.end(); grIt++ ) {
2815       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2816       if ( group && group->Contains( elemInGroups ))
2817         group->SMDSGroup().Add( elemToAdd );
2818     }
2819   }
2820 }
2821
2822
2823 //=======================================================================
2824 //function : RemoveElemFromGroups
2825 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2826 //=======================================================================
2827 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2828                                              SMESHDS_Mesh *          aMesh)
2829 {
2830   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831   if (!groups.empty())
2832   {
2833     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2834     for (; GrIt != groups.end(); GrIt++)
2835     {
2836       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2837       if (!grp || grp->IsEmpty()) continue;
2838       grp->SMDSGroup().Remove(removeelem);
2839     }
2840   }
2841 }
2842
2843 //================================================================================
2844 /*!
2845  * \brief Replace elemToRm by elemToAdd in the all groups
2846  */
2847 //================================================================================
2848
2849 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2850                                             const SMDS_MeshElement* elemToAdd,
2851                                             SMESHDS_Mesh *          aMesh)
2852 {
2853   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2854   if (!groups.empty()) {
2855     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2856     for ( ; grIt != groups.end(); grIt++ ) {
2857       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2858       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2859         group->SMDSGroup().Add( elemToAdd );
2860     }
2861   }
2862 }
2863
2864 //================================================================================
2865 /*!
2866  * \brief Replace elemToRm by elemToAdd in the all groups
2867  */
2868 //================================================================================
2869
2870 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2871                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2872                                             SMESHDS_Mesh *                         aMesh)
2873 {
2874   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2875   if (!groups.empty())
2876   {
2877     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878     for ( ; grIt != groups.end(); grIt++ ) {
2879       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2881         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2882           group->SMDSGroup().Add( elemToAdd[ i ] );
2883     }
2884   }
2885 }
2886
2887 //=======================================================================
2888 //function : QuadToTri
2889 //purpose  : Cut quadrangles into triangles.
2890 //           theCrit is used to select a diagonal to cut
2891 //=======================================================================
2892
2893 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2894                                   const bool         the13Diag)
2895 {
2896   ClearLastCreated();
2897   myLastCreatedElems.reserve( theElems.size() * 2 );
2898
2899   SMESHDS_Mesh *       aMesh = GetMeshDS();
2900   Handle(Geom_Surface) surface;
2901   SMESH_MesherHelper   helper( *GetMesh() );
2902
2903   TIDSortedElemSet::iterator itElem;
2904   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2905   {
2906     const SMDS_MeshElement* elem = *itElem;
2907     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2908       continue;
2909
2910     if ( elem->NbNodes() == 4 ) {
2911       // retrieve element nodes
2912       const SMDS_MeshNode* aNodes [4];
2913       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2914       int i = 0;
2915       while ( itN->more() )
2916         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2917
2918       int aShapeId = FindShape( elem );
2919       const SMDS_MeshElement* newElem1 = 0;
2920       const SMDS_MeshElement* newElem2 = 0;
2921       if ( the13Diag ) {
2922         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2923         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2924       }
2925       else {
2926         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2927         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2928       }
2929       myLastCreatedElems.push_back(newElem1);
2930       myLastCreatedElems.push_back(newElem2);
2931       // put a new triangle on the same shape and add to the same groups
2932       if ( aShapeId )
2933       {
2934         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2935         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2936       }
2937       AddToSameGroups( newElem1, elem, aMesh );
2938       AddToSameGroups( newElem2, elem, aMesh );
2939       aMesh->RemoveElement( elem );
2940     }
2941
2942     // Quadratic quadrangle
2943
2944     else if ( elem->NbNodes() >= 8 )
2945     {
2946       // get surface elem is on
2947       int aShapeId = FindShape( elem );
2948       if ( aShapeId != helper.GetSubShapeID() ) {
2949         surface.Nullify();
2950         TopoDS_Shape shape;
2951         if ( aShapeId > 0 )
2952           shape = aMesh->IndexToShape( aShapeId );
2953         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2954           TopoDS_Face face = TopoDS::Face( shape );
2955           surface = BRep_Tool::Surface( face );
2956           if ( !surface.IsNull() )
2957             helper.SetSubShape( shape );
2958         }
2959       }
2960
2961       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2962       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963       for ( int i = 0; itN->more(); ++i )
2964         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2965
2966       const SMDS_MeshNode* centrNode = aNodes[8];
2967       if ( centrNode == 0 )
2968       {
2969         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2970                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2971                                            surface.IsNull() );
2972         myLastCreatedNodes.push_back(centrNode);
2973       }
2974
2975       // create a new element
2976       const SMDS_MeshElement* newElem1 = 0;
2977       const SMDS_MeshElement* newElem2 = 0;
2978       if ( the13Diag ) {
2979         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2980                                   aNodes[6], aNodes[7], centrNode );
2981         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2982                                   centrNode, aNodes[4], aNodes[5] );
2983       }
2984       else {
2985         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2986                                   aNodes[7], aNodes[4], centrNode );
2987         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2988                                   centrNode, aNodes[5], aNodes[6] );
2989       }
2990       myLastCreatedElems.push_back(newElem1);
2991       myLastCreatedElems.push_back(newElem2);
2992       // put a new triangle on the same shape and add to the same groups
2993       if ( aShapeId )
2994       {
2995         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2996         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2997       }
2998       AddToSameGroups( newElem1, elem, aMesh );
2999       AddToSameGroups( newElem2, elem, aMesh );
3000       aMesh->RemoveElement( elem );
3001     }
3002   }
3003
3004   return true;
3005 }
3006
3007 //=======================================================================
3008 //function : getAngle
3009 //purpose  :
3010 //=======================================================================
3011
3012 double getAngle(const SMDS_MeshElement * tr1,
3013                 const SMDS_MeshElement * tr2,
3014                 const SMDS_MeshNode *    n1,
3015                 const SMDS_MeshNode *    n2)
3016 {
3017   double angle = 2. * M_PI; // bad angle
3018
3019   // get normals
3020   SMESH::Controls::TSequenceOfXYZ P1, P2;
3021   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3022        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3023     return angle;
3024   gp_Vec N1,N2;
3025   if(!tr1->IsQuadratic())
3026     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3027   else
3028     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3029   if ( N1.SquareMagnitude() <= gp::Resolution() )
3030     return angle;
3031   if(!tr2->IsQuadratic())
3032     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3033   else
3034     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3035   if ( N2.SquareMagnitude() <= gp::Resolution() )
3036     return angle;
3037
3038   // find the first diagonal node n1 in the triangles:
3039   // take in account a diagonal link orientation
3040   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3041   for ( int t = 0; t < 2; t++ ) {
3042     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3043     int i = 0, iDiag = -1;
3044     while ( it->more()) {
3045       const SMDS_MeshElement *n = it->next();
3046       if ( n == n1 || n == n2 ) {
3047         if ( iDiag < 0)
3048           iDiag = i;
3049         else {
3050           if ( i - iDiag == 1 )
3051             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3052           else
3053             nFirst[ t ] = n;
3054           break;
3055         }
3056       }
3057       i++;
3058     }
3059   }
3060   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3061     N2.Reverse();
3062
3063   angle = N1.Angle( N2 );
3064   //SCRUTE( angle );
3065   return angle;
3066 }
3067
3068 // =================================================
3069 // class generating a unique ID for a pair of nodes
3070 // and able to return nodes by that ID
3071 // =================================================
3072 class LinkID_Gen {
3073 public:
3074
3075   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3076     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3077   {}
3078
3079   long GetLinkID (const SMDS_MeshNode * n1,
3080                   const SMDS_MeshNode * n2) const
3081   {
3082     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3083   }
3084
3085   bool GetNodes (const long             theLinkID,
3086                  const SMDS_MeshNode* & theNode1,
3087                  const SMDS_MeshNode* & theNode2) const
3088   {
3089     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3090     if ( !theNode1 ) return false;
3091     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3092     if ( !theNode2 ) return false;
3093     return true;
3094   }
3095
3096 private:
3097   LinkID_Gen();
3098   const SMESHDS_Mesh* myMesh;
3099   long                myMaxID;
3100 };
3101
3102
3103 //=======================================================================
3104 //function : TriToQuad
3105 //purpose  : Fuse neighbour triangles into quadrangles.
3106 //           theCrit is used to select a neighbour to fuse with.
3107 //           theMaxAngle is a max angle between element normals at which
3108 //           fusion is still performed.
3109 //=======================================================================
3110
3111 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3112                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3113                                   const double                         theMaxAngle)
3114 {
3115   ClearLastCreated();
3116   myLastCreatedElems.reserve( theElems.size() / 2 );
3117
3118   if ( !theCrit.get() )
3119     return false;
3120
3121   SMESHDS_Mesh * aMesh = GetMeshDS();
3122
3123   // Prepare data for algo: build
3124   // 1. map of elements with their linkIDs
3125   // 2. map of linkIDs with their elements
3126
3127   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3128   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3129   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3130   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3131
3132   TIDSortedElemSet::iterator itElem;
3133   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3134   {
3135     const SMDS_MeshElement* elem = *itElem;
3136     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3137     bool IsTria = ( elem->NbCornerNodes()==3 );
3138     if (!IsTria) continue;
3139
3140     // retrieve element nodes
3141     const SMDS_MeshNode* aNodes [4];
3142     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3143     int i = 0;
3144     while ( i < 3 )
3145       aNodes[ i++ ] = itN->next();
3146     aNodes[ 3 ] = aNodes[ 0 ];
3147
3148     // fill maps
3149     for ( i = 0; i < 3; i++ ) {
3150       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3151       // check if elements sharing a link can be fused
3152       itLE = mapLi_listEl.find( link );
3153       if ( itLE != mapLi_listEl.end() ) {
3154         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3155           continue;
3156         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3157         //if ( FindShape( elem ) != FindShape( elem2 ))
3158         //  continue; // do not fuse triangles laying on different shapes
3159         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3160           continue; // avoid making badly shaped quads
3161         (*itLE).second.push_back( elem );
3162       }
3163       else {
3164         mapLi_listEl[ link ].push_back( elem );
3165       }
3166       mapEl_setLi [ elem ].insert( link );
3167     }
3168   }
3169   // Clean the maps from the links shared by a sole element, ie
3170   // links to which only one element is bound in mapLi_listEl
3171
3172   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3173     int nbElems = (*itLE).second.size();
3174     if ( nbElems < 2  ) {
3175       const SMDS_MeshElement* elem = (*itLE).second.front();
3176       SMESH_TLink link = (*itLE).first;
3177       mapEl_setLi[ elem ].erase( link );
3178       if ( mapEl_setLi[ elem ].empty() )
3179         mapEl_setLi.erase( elem );
3180     }
3181   }
3182
3183   // Algo: fuse triangles into quadrangles
3184
3185   while ( ! mapEl_setLi.empty() ) {
3186     // Look for the start element:
3187     // the element having the least nb of shared links
3188     const SMDS_MeshElement* startElem = 0;
3189     int minNbLinks = 4;
3190     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3191       int nbLinks = (*itEL).second.size();
3192       if ( nbLinks < minNbLinks ) {
3193         startElem = (*itEL).first;
3194         minNbLinks = nbLinks;
3195         if ( minNbLinks == 1 )
3196           break;
3197       }
3198     }
3199
3200     // search elements to fuse starting from startElem or links of elements
3201     // fused earlyer - startLinks
3202     list< SMESH_TLink > startLinks;
3203     while ( startElem || !startLinks.empty() ) {
3204       while ( !startElem && !startLinks.empty() ) {
3205         // Get an element to start, by a link
3206         SMESH_TLink linkId = startLinks.front();
3207         startLinks.pop_front();
3208         itLE = mapLi_listEl.find( linkId );
3209         if ( itLE != mapLi_listEl.end() ) {
3210           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3211           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3212           for ( ; itE != listElem.end() ; itE++ )
3213             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3214               startElem = (*itE);
3215           mapLi_listEl.erase( itLE );
3216         }
3217       }
3218
3219       if ( startElem ) {
3220         // Get candidates to be fused
3221         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3222         const SMESH_TLink *link12 = 0, *link13 = 0;
3223         startElem = 0;
3224         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3225         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3226         ASSERT( !setLi.empty() );
3227         set< SMESH_TLink >::iterator itLi;
3228         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3229         {
3230           const SMESH_TLink & link = (*itLi);
3231           itLE = mapLi_listEl.find( link );
3232           if ( itLE == mapLi_listEl.end() )
3233             continue;
3234
3235           const SMDS_MeshElement* elem = (*itLE).second.front();
3236           if ( elem == tr1 )
3237             elem = (*itLE).second.back();
3238           mapLi_listEl.erase( itLE );
3239           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3240             continue;
3241           if ( tr2 ) {
3242             tr3 = elem;
3243             link13 = &link;
3244           }
3245           else {
3246             tr2 = elem;
3247             link12 = &link;
3248           }
3249
3250           // add other links of elem to list of links to re-start from
3251           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3252           set< SMESH_TLink >::iterator it;
3253           for ( it = links.begin(); it != links.end(); it++ ) {
3254             const SMESH_TLink& link2 = (*it);
3255             if ( link2 != link )
3256               startLinks.push_back( link2 );
3257           }
3258         }
3259
3260         // Get nodes of possible quadrangles
3261         const SMDS_MeshNode *n12 [4], *n13 [4];
3262         bool Ok12 = false, Ok13 = false;
3263         const SMDS_MeshNode *linkNode1, *linkNode2;
3264         if(tr2) {
3265           linkNode1 = link12->first;
3266           linkNode2 = link12->second;
3267           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3268             Ok12 = true;
3269         }
3270         if(tr3) {
3271           linkNode1 = link13->first;
3272           linkNode2 = link13->second;
3273           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3274             Ok13 = true;
3275         }
3276
3277         // Choose a pair to fuse
3278         if ( Ok12 && Ok13 ) {
3279           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3280           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3281           double aBadRate12 = getBadRate( &quad12, theCrit );
3282           double aBadRate13 = getBadRate( &quad13, theCrit );
3283           if (  aBadRate13 < aBadRate12 )
3284             Ok12 = false;
3285           else
3286             Ok13 = false;
3287         }
3288
3289         // Make quadrangles
3290         // and remove fused elems and remove links from the maps
3291         mapEl_setLi.erase( tr1 );
3292         if ( Ok12 )
3293         {
3294           mapEl_setLi.erase( tr2 );
3295           mapLi_listEl.erase( *link12 );
3296           if ( tr1->NbNodes() == 3 )
3297           {
3298             const SMDS_MeshElement* newElem = 0;
3299             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3300             myLastCreatedElems.push_back(newElem);
3301             AddToSameGroups( newElem, tr1, aMesh );
3302             int aShapeId = tr1->getshapeId();
3303             if ( aShapeId )
3304               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3305             aMesh->RemoveElement( tr1 );
3306             aMesh->RemoveElement( tr2 );
3307           }
3308           else {
3309             vector< const SMDS_MeshNode* > N1;
3310             vector< const SMDS_MeshNode* > N2;
3311             getNodesFromTwoTria(tr1,tr2,N1,N2);
3312             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3313             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3314             // i.e. first nodes from both arrays form a new diagonal
3315             const SMDS_MeshNode* aNodes[8];
3316             aNodes[0] = N1[0];
3317             aNodes[1] = N1[1];
3318             aNodes[2] = N2[0];
3319             aNodes[3] = N2[1];
3320             aNodes[4] = N1[3];
3321             aNodes[5] = N2[5];
3322             aNodes[6] = N2[3];
3323             aNodes[7] = N1[5];
3324             const SMDS_MeshElement* newElem = 0;
3325             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3326               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3327                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3328             else
3329               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3330                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3331             myLastCreatedElems.push_back(newElem);
3332             AddToSameGroups( newElem, tr1, aMesh );
3333             int aShapeId = tr1->getshapeId();
3334             if ( aShapeId )
3335               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3336             aMesh->RemoveElement( tr1 );
3337             aMesh->RemoveElement( tr2 );
3338             // remove middle node (9)
3339             if ( N1[4]->NbInverseElements() == 0 )
3340               aMesh->RemoveNode( N1[4] );
3341             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3342               aMesh->RemoveNode( N1[6] );
3343             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3344               aMesh->RemoveNode( N2[6] );
3345           }
3346         }
3347         else if ( Ok13 )
3348         {
3349           mapEl_setLi.erase( tr3 );
3350           mapLi_listEl.erase( *link13 );
3351           if ( tr1->NbNodes() == 3 ) {
3352             const SMDS_MeshElement* newElem = 0;
3353             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3354             myLastCreatedElems.push_back(newElem);
3355             AddToSameGroups( newElem, tr1, aMesh );
3356             int aShapeId = tr1->getshapeId();
3357             if ( aShapeId )
3358               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359             aMesh->RemoveElement( tr1 );
3360             aMesh->RemoveElement( tr3 );
3361           }
3362           else {
3363             vector< const SMDS_MeshNode* > N1;
3364             vector< const SMDS_MeshNode* > N2;
3365             getNodesFromTwoTria(tr1,tr3,N1,N2);
3366             // now we receive following N1 and N2 (using numeration as above image)
3367             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3368             // i.e. first nodes from both arrays form a new diagonal
3369             const SMDS_MeshNode* aNodes[8];
3370             aNodes[0] = N1[0];
3371             aNodes[1] = N1[1];
3372             aNodes[2] = N2[0];
3373             aNodes[3] = N2[1];
3374             aNodes[4] = N1[3];
3375             aNodes[5] = N2[5];
3376             aNodes[6] = N2[3];
3377             aNodes[7] = N1[5];
3378             const SMDS_MeshElement* newElem = 0;
3379             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3382             else
3383               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385             myLastCreatedElems.push_back(newElem);
3386             AddToSameGroups( newElem, tr1, aMesh );
3387             int aShapeId = tr1->getshapeId();
3388             if ( aShapeId )
3389               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390             aMesh->RemoveElement( tr1 );
3391             aMesh->RemoveElement( tr3 );
3392             // remove middle node (9)
3393             if ( N1[4]->NbInverseElements() == 0 )
3394               aMesh->RemoveNode( N1[4] );
3395             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396               aMesh->RemoveNode( N1[6] );
3397             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N2[6] );
3399           }
3400         }
3401
3402         // Next element to fuse: the rejected one
3403         if ( tr3 )
3404           startElem = Ok12 ? tr3 : tr2;
3405
3406       } // if ( startElem )
3407     } // while ( startElem || !startLinks.empty() )
3408   } // while ( ! mapEl_setLi.empty() )
3409
3410   return true;
3411 }
3412
3413 //================================================================================
3414 /*!
3415  * \brief Return nodes linked to the given one
3416  * \param theNode - the node
3417  * \param linkedNodes - the found nodes
3418  * \param type - the type of elements to check
3419  *
3420  * Medium nodes are ignored
3421  */
3422 //================================================================================
3423
3424 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3425                                        TIDSortedElemSet &   linkedNodes,
3426                                        SMDSAbs_ElementType  type )
3427 {
3428   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3429   while ( elemIt->more() )
3430   {
3431     const SMDS_MeshElement* elem = elemIt->next();
3432     if(elem->GetType() == SMDSAbs_0DElement)
3433       continue;
3434
3435     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3436     if ( elem->GetType() == SMDSAbs_Volume )
3437     {
3438       SMDS_VolumeTool vol( elem );
3439       while ( nodeIt->more() ) {
3440         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3441         if ( theNode != n && vol.IsLinked( theNode, n ))
3442           linkedNodes.insert( n );
3443       }
3444     }
3445     else
3446     {
3447       for ( int i = 0; nodeIt->more(); ++i ) {
3448         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3449         if ( n == theNode ) {
3450           int iBefore = i - 1;
3451           int iAfter  = i + 1;
3452           if ( elem->IsQuadratic() ) {
3453             int nb = elem->NbNodes() / 2;
3454             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3455             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3456           }
3457           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3458           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3459         }
3460       }
3461     }
3462   }
3463 }
3464
3465 //=======================================================================
3466 //function : laplacianSmooth
3467 //purpose  : pulls theNode toward the center of surrounding nodes directly
3468 //           connected to that node along an element edge
3469 //=======================================================================
3470
3471 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3472                      const Handle(Geom_Surface)&          theSurface,
3473                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3474 {
3475   // find surrounding nodes
3476
3477   TIDSortedElemSet nodeSet;
3478   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3479
3480   // compute new coodrs
3481
3482   double coord[] = { 0., 0., 0. };
3483   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3484   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3485     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3486     if ( theSurface.IsNull() ) { // smooth in 3D
3487       coord[0] += node->X();
3488       coord[1] += node->Y();
3489       coord[2] += node->Z();
3490     }
3491     else { // smooth in 2D
3492       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3493       gp_XY* uv = theUVMap[ node ];
3494       coord[0] += uv->X();
3495       coord[1] += uv->Y();
3496     }
3497   }
3498   int nbNodes = nodeSet.size();
3499   if ( !nbNodes )
3500     return;
3501   coord[0] /= nbNodes;
3502   coord[1] /= nbNodes;
3503
3504   if ( !theSurface.IsNull() ) {
3505     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3506     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3507     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3508     coord[0] = p3d.X();
3509     coord[1] = p3d.Y();
3510     coord[2] = p3d.Z();
3511   }
3512   else
3513     coord[2] /= nbNodes;
3514
3515   // move node
3516
3517   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3518 }
3519
3520 //=======================================================================
3521 //function : centroidalSmooth
3522 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3523 //           surrounding elements
3524 //=======================================================================
3525
3526 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3527                       const Handle(Geom_Surface)&          theSurface,
3528                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3529 {
3530   gp_XYZ aNewXYZ(0.,0.,0.);
3531   SMESH::Controls::Area anAreaFunc;
3532   double totalArea = 0.;
3533   int nbElems = 0;
3534
3535   // compute new XYZ
3536
3537   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3538   while ( elemIt->more() )
3539   {
3540     const SMDS_MeshElement* elem = elemIt->next();
3541     nbElems++;
3542
3543     gp_XYZ elemCenter(0.,0.,0.);
3544     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3545     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3546     int nn = elem->NbNodes();
3547     if(elem->IsQuadratic()) nn = nn/2;
3548     int i=0;
3549     //while ( itN->more() ) {
3550     while ( i<nn ) {
3551       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3552       i++;
3553       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3554       aNodePoints.push_back( aP );
3555       if ( !theSurface.IsNull() ) { // smooth in 2D
3556         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3557         gp_XY* uv = theUVMap[ aNode ];
3558         aP.SetCoord( uv->X(), uv->Y(), 0. );
3559       }
3560       elemCenter += aP;
3561     }
3562     double elemArea = anAreaFunc.GetValue( aNodePoints );
3563     totalArea += elemArea;
3564     elemCenter /= nn;
3565     aNewXYZ += elemCenter * elemArea;
3566   }
3567   aNewXYZ /= totalArea;
3568   if ( !theSurface.IsNull() ) {
3569     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3570     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3571   }
3572
3573   // move node
3574
3575   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3576 }
3577
3578 //=======================================================================
3579 //function : getClosestUV
3580 //purpose  : return UV of closest projection
3581 //=======================================================================
3582
3583 static bool getClosestUV (Extrema_GenExtPS& projector,
3584                           const gp_Pnt&     point,
3585                           gp_XY &           result)
3586 {
3587   projector.Perform( point );
3588   if ( projector.IsDone() ) {
3589     double u, v, minVal = DBL_MAX;
3590     for ( int i = projector.NbExt(); i > 0; i-- )
3591       if ( projector.SquareDistance( i ) < minVal ) {
3592         minVal = projector.SquareDistance( i );
3593         projector.Point( i ).Parameter( u, v );
3594       }
3595     result.SetCoord( u, v );
3596     return true;
3597   }
3598   return false;
3599 }
3600
3601 //=======================================================================
3602 //function : Smooth
3603 //purpose  : Smooth theElements during theNbIterations or until a worst
3604 //           element has aspect ratio <= theTgtAspectRatio.
3605 //           Aspect Ratio varies in range [1.0, inf].
3606 //           If theElements is empty, the whole mesh is smoothed.
3607 //           theFixedNodes contains additionally fixed nodes. Nodes built
3608 //           on edges and boundary nodes are always fixed.
3609 //=======================================================================
3610
3611 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3612                                set<const SMDS_MeshNode*> & theFixedNodes,
3613                                const SmoothMethod          theSmoothMethod,
3614                                const int                   theNbIterations,
3615                                double                      theTgtAspectRatio,
3616                                const bool                  the2D)
3617 {
3618   ClearLastCreated();
3619
3620   if ( theTgtAspectRatio < 1.0 )
3621     theTgtAspectRatio = 1.0;
3622
3623   const double disttol = 1.e-16;
3624
3625   SMESH::Controls::AspectRatio aQualityFunc;
3626
3627   SMESHDS_Mesh* aMesh = GetMeshDS();
3628
3629   if ( theElems.empty() ) {
3630     // add all faces to theElems
3631     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3632     while ( fIt->more() ) {
3633       const SMDS_MeshElement* face = fIt->next();
3634       theElems.insert( theElems.end(), face );
3635     }
3636   }
3637   // get all face ids theElems are on
3638   set< int > faceIdSet;
3639   TIDSortedElemSet::iterator itElem;
3640   if ( the2D )
3641     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3642       int fId = FindShape( *itElem );
3643       // check that corresponding submesh exists and a shape is face
3644       if (fId &&
3645           faceIdSet.find( fId ) == faceIdSet.end() &&
3646           aMesh->MeshElements( fId )) {
3647         TopoDS_Shape F = aMesh->IndexToShape( fId );
3648         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3649           faceIdSet.insert( fId );
3650       }
3651     }
3652   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3653
3654   // ===============================================
3655   // smooth elements on each TopoDS_Face separately
3656   // ===============================================
3657
3658   SMESH_MesherHelper helper( *GetMesh() );
3659
3660   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3661   for ( ; fId != faceIdSet.rend(); ++fId )
3662   {
3663     // get face surface and submesh
3664     Handle(Geom_Surface) surface;
3665     SMESHDS_SubMesh* faceSubMesh = 0;
3666     TopoDS_Face face;
3667     double fToler2 = 0;
3668     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3669     bool isUPeriodic = false, isVPeriodic = false;
3670     if ( *fId )
3671     {
3672       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3673       surface = BRep_Tool::Surface( face );
3674       faceSubMesh = aMesh->MeshElements( *fId );
3675       fToler2 = BRep_Tool::Tolerance( face );
3676       fToler2 *= fToler2 * 10.;
3677       isUPeriodic = surface->IsUPeriodic();
3678       // if ( isUPeriodic )
3679       //   surface->UPeriod();
3680       isVPeriodic = surface->IsVPeriodic();
3681       // if ( isVPeriodic )
3682       //   surface->VPeriod();
3683       surface->Bounds( u1, u2, v1, v2 );
3684       helper.SetSubShape( face );
3685     }
3686     // ---------------------------------------------------------
3687     // for elements on a face, find movable and fixed nodes and
3688     // compute UV for them
3689     // ---------------------------------------------------------
3690     bool checkBoundaryNodes = false;
3691     bool isQuadratic = false;
3692     set<const SMDS_MeshNode*> setMovableNodes;
3693     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3694     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3695     list< const SMDS_MeshElement* > elemsOnFace;
3696
3697     Extrema_GenExtPS projector;
3698     GeomAdaptor_Surface surfAdaptor;
3699     if ( !surface.IsNull() ) {
3700       surfAdaptor.Load( surface );
3701       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3702     }
3703     int nbElemOnFace = 0;
3704     itElem = theElems.begin();
3705     // loop on not yet smoothed elements: look for elems on a face
3706     while ( itElem != theElems.end() )
3707     {
3708       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3709         break; // all elements found
3710
3711       const SMDS_MeshElement* elem = *itElem;
3712       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3713            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3714         ++itElem;
3715         continue;
3716       }
3717       elemsOnFace.push_back( elem );
3718       theElems.erase( itElem++ );
3719       nbElemOnFace++;
3720
3721       if ( !isQuadratic )
3722         isQuadratic = elem->IsQuadratic();
3723
3724       // get movable nodes of elem
3725       const SMDS_MeshNode* node;
3726       SMDS_TypeOfPosition posType;
3727       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3728       int nn = 0, nbn =  elem->NbNodes();
3729       if(elem->IsQuadratic())
3730         nbn = nbn/2;
3731       while ( nn++ < nbn ) {
3732         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3733         const SMDS_PositionPtr& pos = node->GetPosition();
3734         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3735         if (posType != SMDS_TOP_EDGE &&
3736             posType != SMDS_TOP_VERTEX &&
3737             theFixedNodes.find( node ) == theFixedNodes.end())
3738         {
3739           // check if all faces around the node are on faceSubMesh
3740           // because a node on edge may be bound to face
3741           bool all = true;
3742           if ( faceSubMesh ) {
3743             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744             while ( eIt->more() && all ) {
3745               const SMDS_MeshElement* e = eIt->next();
3746               all = faceSubMesh->Contains( e );
3747             }
3748           }
3749           if ( all )
3750             setMovableNodes.insert( node );
3751           else
3752             checkBoundaryNodes = true;
3753         }
3754         if ( posType == SMDS_TOP_3DSPACE )
3755           checkBoundaryNodes = true;
3756       }
3757
3758       if ( surface.IsNull() )
3759         continue;
3760
3761       // get nodes to check UV
3762       list< const SMDS_MeshNode* > uvCheckNodes;
3763       const SMDS_MeshNode* nodeInFace = 0;
3764       itN = elem->nodesIterator();
3765       nn = 0; nbn =  elem->NbNodes();
3766       if(elem->IsQuadratic())
3767         nbn = nbn/2;
3768       while ( nn++ < nbn ) {
3769         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3770         if ( node->GetPosition()->GetDim() == 2 )
3771           nodeInFace = node;
3772         if ( uvMap.find( node ) == uvMap.end() )
3773           uvCheckNodes.push_back( node );
3774         // add nodes of elems sharing node
3775         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3776         //         while ( eIt->more() ) {
3777         //           const SMDS_MeshElement* e = eIt->next();
3778         //           if ( e != elem ) {
3779         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3780         //             while ( nIt->more() ) {
3781         //               const SMDS_MeshNode* n =
3782         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3783         //               if ( uvMap.find( n ) == uvMap.end() )
3784         //                 uvCheckNodes.push_back( n );
3785         //             }
3786         //           }
3787         //         }
3788       }
3789       // check UV on face
3790       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3791       for ( ; n != uvCheckNodes.end(); ++n ) {
3792         node = *n;
3793         gp_XY uv( 0, 0 );
3794         const SMDS_PositionPtr& pos = node->GetPosition();
3795         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3796         // get existing UV
3797         if ( pos )
3798         {
3799           bool toCheck = true;
3800           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3801         }
3802         // compute not existing UV
3803         bool project = ( posType == SMDS_TOP_3DSPACE );
3804         // double dist1 = DBL_MAX, dist2 = 0;
3805         // if ( posType != SMDS_TOP_3DSPACE ) {
3806         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3807         //   project = dist1 > fToler2;
3808         // }
3809         if ( project ) { // compute new UV
3810           gp_XY newUV;
3811           gp_Pnt pNode = SMESH_NodeXYZ( node );
3812           if ( !getClosestUV( projector, pNode, newUV )) {
3813             MESSAGE("Node Projection Failed " << node);
3814           }
3815           else {
3816             if ( isUPeriodic )
3817               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3818             if ( isVPeriodic )
3819               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3820             // check new UV
3821             // if ( posType != SMDS_TOP_3DSPACE )
3822             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3823             // if ( dist2 < dist1 )
3824             uv = newUV;
3825           }
3826         }
3827         // store UV in the map
3828         listUV.push_back( uv );
3829         uvMap.insert( make_pair( node, &listUV.back() ));
3830       }
3831     } // loop on not yet smoothed elements
3832
3833     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3834       checkBoundaryNodes = true;
3835
3836     // fix nodes on mesh boundary
3837
3838     if ( checkBoundaryNodes ) {
3839       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3840       map< SMESH_TLink, int >::iterator link_nb;
3841       // put all elements links to linkNbMap
3842       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3843       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3844         const SMDS_MeshElement* elem = (*elemIt);
3845         int nbn =  elem->NbCornerNodes();
3846         // loop on elem links: insert them in linkNbMap
3847         for ( int iN = 0; iN < nbn; ++iN ) {
3848           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3849           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3850           SMESH_TLink link( n1, n2 );
3851           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3852           link_nb->second++;
3853         }
3854       }
3855       // remove nodes that are in links encountered only once from setMovableNodes
3856       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3857         if ( link_nb->second == 1 ) {
3858           setMovableNodes.erase( link_nb->first.node1() );
3859           setMovableNodes.erase( link_nb->first.node2() );
3860         }
3861       }
3862     }
3863
3864     // -----------------------------------------------------
3865     // for nodes on seam edge, compute one more UV ( uvMap2 );
3866     // find movable nodes linked to nodes on seam and which
3867     // are to be smoothed using the second UV ( uvMap2 )
3868     // -----------------------------------------------------
3869
3870     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3871     if ( !surface.IsNull() ) {
3872       TopExp_Explorer eExp( face, TopAbs_EDGE );
3873       for ( ; eExp.More(); eExp.Next() ) {
3874         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3875         if ( !BRep_Tool::IsClosed( edge, face ))
3876           continue;
3877         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3878         if ( !sm ) continue;
3879         // find out which parameter varies for a node on seam
3880         double f,l;
3881         gp_Pnt2d uv1, uv2;
3882         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3883         if ( pcurve.IsNull() ) continue;
3884         uv1 = pcurve->Value( f );
3885         edge.Reverse();
3886         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3887         if ( pcurve.IsNull() ) continue;
3888         uv2 = pcurve->Value( f );
3889         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3890         // assure uv1 < uv2
3891         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3892           std::swap( uv1, uv2 );
3893         // get nodes on seam and its vertices
3894         list< const SMDS_MeshNode* > seamNodes;
3895         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3896         while ( nSeamIt->more() ) {
3897           const SMDS_MeshNode* node = nSeamIt->next();
3898           if ( !isQuadratic || !IsMedium( node ))
3899             seamNodes.push_back( node );
3900         }
3901         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3902         for ( ; vExp.More(); vExp.Next() ) {
3903           sm = aMesh->MeshElements( vExp.Current() );
3904           if ( sm ) {
3905             nSeamIt = sm->GetNodes();
3906             while ( nSeamIt->more() )
3907               seamNodes.push_back( nSeamIt->next() );
3908           }
3909         }
3910         // loop on nodes on seam
3911         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3912         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3913           const SMDS_MeshNode* nSeam = *noSeIt;
3914           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3915           if ( n_uv == uvMap.end() )
3916             continue;
3917           // set the first UV
3918           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3919           // set the second UV
3920           listUV.push_back( *n_uv->second );
3921           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3922           if ( uvMap2.empty() )
3923             uvMap2 = uvMap; // copy the uvMap contents
3924           uvMap2[ nSeam ] = &listUV.back();
3925
3926           // collect movable nodes linked to ones on seam in nodesNearSeam
3927           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3928           while ( eIt->more() ) {
3929             const SMDS_MeshElement* e = eIt->next();
3930             int nbUseMap1 = 0, nbUseMap2 = 0;
3931             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3932             int nn = 0, nbn =  e->NbNodes();
3933             if(e->IsQuadratic()) nbn = nbn/2;
3934             while ( nn++ < nbn )
3935             {
3936               const SMDS_MeshNode* n =
3937                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3938               if (n == nSeam ||
3939                   setMovableNodes.find( n ) == setMovableNodes.end() )
3940                 continue;
3941               // add only nodes being closer to uv2 than to uv1
3942               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3943               //              0.5 * ( n->Y() + nSeam->Y() ),
3944               //              0.5 * ( n->Z() + nSeam->Z() ));
3945               // gp_XY uv;
3946               // getClosestUV( projector, pMid, uv );
3947               double x = uvMap[ n ]->Coord( iPar );
3948               if ( Abs( uv1.Coord( iPar ) - x ) >
3949                    Abs( uv2.Coord( iPar ) - x )) {
3950                 nodesNearSeam.insert( n );
3951                 nbUseMap2++;
3952               }
3953               else
3954                 nbUseMap1++;
3955             }
3956             // for centroidalSmooth all element nodes must
3957             // be on one side of a seam
3958             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3959               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3960               nn = 0;
3961               while ( nn++ < nbn ) {
3962                 const SMDS_MeshNode* n =
3963                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3964                 setMovableNodes.erase( n );
3965               }
3966             }
3967           }
3968         } // loop on nodes on seam
3969       } // loop on edge of a face
3970     } // if ( !face.IsNull() )
3971
3972     if ( setMovableNodes.empty() ) {
3973       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3974       continue; // goto next face
3975     }
3976
3977     // -------------
3978     // SMOOTHING //
3979     // -------------
3980
3981     int it = -1;
3982     double maxRatio = -1., maxDisplacement = -1.;
3983     set<const SMDS_MeshNode*>::iterator nodeToMove;
3984     for ( it = 0; it < theNbIterations; it++ ) {
3985       maxDisplacement = 0.;
3986       nodeToMove = setMovableNodes.begin();
3987       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3988         const SMDS_MeshNode* node = (*nodeToMove);
3989         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3990
3991         // smooth
3992         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3993         if ( theSmoothMethod == LAPLACIAN )
3994           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3995         else
3996           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3997
3998         // node displacement
3999         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4000         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4001         if ( aDispl > maxDisplacement )
4002           maxDisplacement = aDispl;
4003       }
4004       // no node movement => exit
4005       //if ( maxDisplacement < 1.e-16 ) {
4006       if ( maxDisplacement < disttol ) {
4007         MESSAGE("-- no node movement --");
4008         break;
4009       }
4010
4011       // check elements quality
4012       maxRatio  = 0;
4013       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4014       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4015         const SMDS_MeshElement* elem = (*elemIt);
4016         if ( !elem || elem->GetType() != SMDSAbs_Face )
4017           continue;
4018         SMESH::Controls::TSequenceOfXYZ aPoints;
4019         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4020           double aValue = aQualityFunc.GetValue( aPoints );
4021           if ( aValue > maxRatio )
4022             maxRatio = aValue;
4023         }
4024       }
4025       if ( maxRatio <= theTgtAspectRatio ) {
4026         //MESSAGE("-- quality achieved --");
4027         break;
4028       }
4029       if (it+1 == theNbIterations) {
4030         //MESSAGE("-- Iteration limit exceeded --");
4031       }
4032     } // smoothing iterations
4033
4034     // MESSAGE(" Face id: " << *fId <<
4035     //         " Nb iterstions: " << it <<
4036     //         " Displacement: " << maxDisplacement <<
4037     //         " Aspect Ratio " << maxRatio);
4038
4039     // ---------------------------------------
4040     // new nodes positions are computed,
4041     // record movement in DS and set new UV
4042     // ---------------------------------------
4043     nodeToMove = setMovableNodes.begin();
4044     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4045       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4046       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4047       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4048       if ( node_uv != uvMap.end() ) {
4049         gp_XY* uv = node_uv->second;
4050         node->SetPosition
4051           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4052       }
4053     }
4054
4055     // move medium nodes of quadratic elements
4056     if ( isQuadratic )
4057     {
4058       vector<const SMDS_MeshNode*> nodes;
4059       bool checkUV;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4062       {
4063         const SMDS_MeshElement* QF = *elemIt;
4064         if ( QF->IsQuadratic() )
4065         {
4066           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4067                         SMDS_MeshElement::iterator() );
4068           nodes.push_back( nodes[0] );
4069           gp_Pnt xyz;
4070           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4071           {
4072             if ( !surface.IsNull() )
4073             {
4074               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4075               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4076               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4077               xyz = surface->Value( uv.X(), uv.Y() );
4078             }
4079             else {
4080               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4081             }
4082             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4083               // we have to move a medium node
4084               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4085           }
4086         }
4087       }
4088     }
4089
4090   } // loop on face ids
4091
4092 }
4093
4094 namespace
4095 {
4096   //=======================================================================
4097   //function : isReverse
4098   //purpose  : Return true if normal of prevNodes is not co-directied with
4099   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4100   //           iNotSame is where prevNodes and nextNodes are different.
4101   //           If result is true then future volume orientation is OK
4102   //=======================================================================
4103
4104   bool isReverse(const SMDS_MeshElement*             face,
4105                  const vector<const SMDS_MeshNode*>& prevNodes,
4106                  const vector<const SMDS_MeshNode*>& nextNodes,
4107                  const int                           iNotSame)
4108   {
4109
4110     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4111     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4112     gp_XYZ extrDir( pN - pP ), faceNorm;
4113     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4114
4115     return faceNorm * extrDir < 0.0;
4116   }
4117
4118   //================================================================================
4119   /*!
4120    * \brief Assure that theElemSets[0] holds elements, not nodes
4121    */
4122   //================================================================================
4123
4124   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4125   {
4126     if ( !theElemSets[0].empty() &&
4127          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4128     {
4129       std::swap( theElemSets[0], theElemSets[1] );
4130     }
4131     else if ( !theElemSets[1].empty() &&
4132               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4133     {
4134       std::swap( theElemSets[0], theElemSets[1] );
4135     }
4136   }
4137 }
4138
4139 //=======================================================================
4140 /*!
4141  * \brief Create elements by sweeping an element
4142  * \param elem - element to sweep
4143  * \param newNodesItVec - nodes generated from each node of the element
4144  * \param newElems - generated elements
4145  * \param nbSteps - number of sweeping steps
4146  * \param srcElements - to append elem for each generated element
4147  */
4148 //=======================================================================
4149
4150 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4151                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4152                                     list<const SMDS_MeshElement*>&        newElems,
4153                                     const size_t                          nbSteps,
4154                                     SMESH_SequenceOfElemPtr&              srcElements)
4155 {
4156   SMESHDS_Mesh* aMesh = GetMeshDS();
4157
4158   const int           nbNodes = elem->NbNodes();
4159   const int         nbCorners = elem->NbCornerNodes();
4160   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4161                                                           polyhedron creation !!! */
4162   // Loop on elem nodes:
4163   // find new nodes and detect same nodes indices
4164   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4165   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4166   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4167   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4168
4169   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4170   vector<int> sames(nbNodes);
4171   vector<bool> isSingleNode(nbNodes);
4172
4173   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4174     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4175     const SMDS_MeshNode*                         node = nnIt->first;
4176     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4177     if ( listNewNodes.empty() )
4178       return;
4179
4180     itNN   [ iNode ] = listNewNodes.begin();
4181     prevNod[ iNode ] = node;
4182     nextNod[ iNode ] = listNewNodes.front();
4183
4184     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4185                                                              corner node of linear */
4186     if ( prevNod[ iNode ] != nextNod [ iNode ])
4187       nbDouble += !isSingleNode[iNode];
4188
4189     if( iNode < nbCorners ) { // check corners only
4190       if ( prevNod[ iNode ] == nextNod [ iNode ])
4191         sames[nbSame++] = iNode;
4192       else
4193         iNotSameNode = iNode;
4194     }
4195   }
4196
4197   if ( nbSame == nbNodes || nbSame > 2) {
4198     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4199     return;
4200   }
4201
4202   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4203   {
4204     // fix nodes order to have bottom normal external
4205     if ( baseType == SMDSEntity_Polygon )
4206     {
4207       std::reverse( itNN.begin(), itNN.end() );
4208       std::reverse( prevNod.begin(), prevNod.end() );
4209       std::reverse( midlNod.begin(), midlNod.end() );
4210       std::reverse( nextNod.begin(), nextNod.end() );
4211       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4212     }
4213     else
4214     {
4215       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4216       SMDS_MeshCell::applyInterlace( ind, itNN );
4217       SMDS_MeshCell::applyInterlace( ind, prevNod );
4218       SMDS_MeshCell::applyInterlace( ind, nextNod );
4219       SMDS_MeshCell::applyInterlace( ind, midlNod );
4220       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4221       if ( nbSame > 0 )
4222       {
4223         sames[nbSame] = iNotSameNode;
4224         for ( int j = 0; j <= nbSame; ++j )
4225           for ( size_t i = 0; i < ind.size(); ++i )
4226             if ( ind[i] == sames[j] )
4227             {
4228               sames[j] = i;
4229               break;
4230             }
4231         iNotSameNode = sames[nbSame];
4232       }
4233     }
4234   }
4235   else if ( elem->GetType() == SMDSAbs_Edge )
4236   {
4237     // orient a new face same as adjacent one
4238     int i1, i2;
4239     const SMDS_MeshElement* e;
4240     TIDSortedElemSet dummy;
4241     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4242         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4243         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4244     {
4245       // there is an adjacent face, check order of nodes in it
4246       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4247       if ( sameOrder )
4248       {
4249         std::swap( itNN[0],    itNN[1] );
4250         std::swap( prevNod[0], prevNod[1] );
4251         std::swap( nextNod[0], nextNod[1] );
4252         std::swap( isSingleNode[0], isSingleNode[1] );
4253         if ( nbSame > 0 )
4254           sames[0] = 1 - sames[0];
4255         iNotSameNode = 1 - iNotSameNode;
4256       }
4257     }
4258   }
4259
4260   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4261   if ( nbSame > 0 ) {
4262     iSameNode    = sames[ nbSame-1 ];
4263     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4264     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4265     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4266   }
4267
4268   if ( baseType == SMDSEntity_Polygon )
4269   {
4270     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4271     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4272   }
4273   else if ( baseType == SMDSEntity_Quad_Polygon )
4274   {
4275     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4276     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4277   }
4278
4279   // make new elements
4280   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4281   {
4282     // get next nodes
4283     for ( iNode = 0; iNode < nbNodes; iNode++ )
4284     {
4285       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4286       nextNod[ iNode ] = *itNN[ iNode ]++;
4287     }
4288
4289     SMDS_MeshElement* aNewElem = 0;
4290     /*if(!elem->IsPoly())*/ {
4291       switch ( baseType ) {
4292       case SMDSEntity_0D:
4293       case SMDSEntity_Node: { // sweep NODE
4294         if ( nbSame == 0 ) {
4295           if ( isSingleNode[0] )
4296             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4297           else
4298             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4299         }
4300         else
4301           return;
4302         break;
4303       }
4304       case SMDSEntity_Edge: { // sweep EDGE
4305         if ( nbDouble == 0 )
4306         {
4307           if ( nbSame == 0 ) // ---> quadrangle
4308             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4309                                       nextNod[ 1 ], nextNod[ 0 ] );
4310           else               // ---> triangle
4311             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4312                                       nextNod[ iNotSameNode ] );
4313         }
4314         else                 // ---> polygon
4315         {
4316           vector<const SMDS_MeshNode*> poly_nodes;
4317           poly_nodes.push_back( prevNod[0] );
4318           poly_nodes.push_back( prevNod[1] );
4319           if ( prevNod[1] != nextNod[1] )
4320           {
4321             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4322             poly_nodes.push_back( nextNod[1] );
4323           }
4324           if ( prevNod[0] != nextNod[0] )
4325           {
4326             poly_nodes.push_back( nextNod[0] );
4327             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4328           }
4329           switch ( poly_nodes.size() ) {
4330           case 3:
4331             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4332             break;
4333           case 4:
4334             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4335                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4336             break;
4337           default:
4338             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4339           }
4340         }
4341         break;
4342       }
4343       case SMDSEntity_Triangle: // TRIANGLE --->
4344       {
4345         if ( nbDouble > 0 ) break;
4346         if ( nbSame == 0 )       // ---> pentahedron
4347           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4348                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4349
4350         else if ( nbSame == 1 )  // ---> pyramid
4351           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4352                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4353                                        nextNod[ iSameNode ]);
4354
4355         else // 2 same nodes:       ---> tetrahedron
4356           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4357                                        nextNod[ iNotSameNode ]);
4358         break;
4359       }
4360       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4361       {
4362         if ( nbSame == 2 )
4363           return;
4364         if ( nbDouble+nbSame == 2 )
4365         {
4366           if(nbSame==0) {      // ---> quadratic quadrangle
4367             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4368                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4369           }
4370           else { //(nbSame==1) // ---> quadratic triangle
4371             if(sames[0]==2) {
4372               return; // medium node on axis
4373             }
4374             else if(sames[0]==0)
4375               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4376                                         prevNod[2], midlNod[1], nextNod[2] );
4377             else // sames[0]==1
4378               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4379                                         prevNod[2], nextNod[2], midlNod[0]);
4380           }
4381         }
4382         else if ( nbDouble == 3 )
4383         {
4384           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4385             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4387           }
4388         }
4389         else
4390           return;
4391         break;
4392       }
4393       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4394         if ( nbDouble > 0 ) break;
4395
4396         if ( nbSame == 0 )       // ---> hexahedron
4397           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4398                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4399
4400         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4401           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4402                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4403                                        nextNod[ iSameNode ]);
4404           newElems.push_back( aNewElem );
4405           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4406                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4407                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4408         }
4409         else if ( nbSame == 2 ) { // ---> pentahedron
4410           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4411             // iBeforeSame is same too
4412             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4413                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4414                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4415           else
4416             // iAfterSame is same too
4417             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4418                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4420         }
4421         break;
4422       }
4423       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4424       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4425         if ( nbDouble+nbSame != 3 ) break;
4426         if(nbSame==0) {
4427           // --->  pentahedron with 15 nodes
4428           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4429                                        nextNod[0], nextNod[1], nextNod[2],
4430                                        prevNod[3], prevNod[4], prevNod[5],
4431                                        nextNod[3], nextNod[4], nextNod[5],
4432                                        midlNod[0], midlNod[1], midlNod[2]);
4433         }
4434         else if(nbSame==1) {
4435           // --->  2d order pyramid of 13 nodes
4436           int apex = iSameNode;
4437           int i0 = ( apex + 1 ) % nbCorners;
4438           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4439           int i0a = apex + 3;
4440           int i1a = i1 + 3;
4441           int i01 = i0 + 3;
4442           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4443                                       nextNod[i0], nextNod[i1], prevNod[apex],
4444                                       prevNod[i01], midlNod[i0],
4445                                       nextNod[i01], midlNod[i1],
4446                                       prevNod[i1a], prevNod[i0a],
4447                                       nextNod[i0a], nextNod[i1a]);
4448         }
4449         else if(nbSame==2) {
4450           // --->  2d order tetrahedron of 10 nodes
4451           int n1 = iNotSameNode;
4452           int n2 = ( n1 + 1             ) % nbCorners;
4453           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4454           int n12 = n1 + 3;
4455           int n23 = n2 + 3;
4456           int n31 = n3 + 3;
4457           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4458                                        prevNod[n12], prevNod[n23], prevNod[n31],
4459                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4460         }
4461         break;
4462       }
4463       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4464         if( nbSame == 0 ) {
4465           if ( nbDouble != 4 ) break;
4466           // --->  hexahedron with 20 nodes
4467           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4468                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4469                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4470                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4471                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4472         }
4473         else if(nbSame==1) {
4474           // ---> pyramid + pentahedron - can not be created since it is needed
4475           // additional middle node at the center of face
4476           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4477           return;
4478         }
4479         else if( nbSame == 2 ) {
4480           if ( nbDouble != 2 ) break;
4481           // --->  2d order Pentahedron with 15 nodes
4482           int n1,n2,n4,n5;
4483           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4484             // iBeforeSame is same too
4485             n1 = iBeforeSame;
4486             n2 = iOpposSame;
4487             n4 = iSameNode;
4488             n5 = iAfterSame;
4489           }
4490           else {
4491             // iAfterSame is same too
4492             n1 = iSameNode;
4493             n2 = iBeforeSame;
4494             n4 = iAfterSame;
4495             n5 = iOpposSame;
4496           }
4497           int n12 = n2 + 4;
4498           int n45 = n4 + 4;
4499           int n14 = n1 + 4;
4500           int n25 = n5 + 4;
4501           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4502                                        prevNod[n4], prevNod[n5], nextNod[n5],
4503                                        prevNod[n12], midlNod[n2], nextNod[n12],
4504                                        prevNod[n45], midlNod[n5], nextNod[n45],
4505                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4506         }
4507         break;
4508       }
4509       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4510
4511         if( nbSame == 0 && nbDouble == 9 ) {
4512           // --->  tri-quadratic hexahedron with 27 nodes
4513           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4514                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4515                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4516                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4517                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4518                                        prevNod[8], // bottom center
4519                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4520                                        nextNod[8], // top center
4521                                        midlNod[8]);// elem center
4522         }
4523         else
4524         {
4525           return;
4526         }
4527         break;
4528       }
4529       case SMDSEntity_Polygon: { // sweep POLYGON
4530
4531         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4532           // --->  hexagonal prism
4533           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4534                                        prevNod[3], prevNod[4], prevNod[5],
4535                                        nextNod[0], nextNod[1], nextNod[2],
4536                                        nextNod[3], nextNod[4], nextNod[5]);
4537         }
4538         break;
4539       }
4540       case SMDSEntity_Ball:
4541         return;
4542
4543       default:
4544         break;
4545       } // switch ( baseType )
4546     } // scope
4547
4548     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4549     {
4550       if ( baseType != SMDSEntity_Polygon )
4551       {
4552         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4553         SMDS_MeshCell::applyInterlace( ind, prevNod );
4554         SMDS_MeshCell::applyInterlace( ind, nextNod );
4555         SMDS_MeshCell::applyInterlace( ind, midlNod );
4556         SMDS_MeshCell::applyInterlace( ind, itNN );
4557         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4558         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4559       }
4560       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4561       vector<int> quantities (nbNodes + 2);
4562       polyedre_nodes.clear();
4563       quantities.clear();
4564
4565       // bottom of prism
4566       for (int inode = 0; inode < nbNodes; inode++)
4567         polyedre_nodes.push_back( prevNod[inode] );
4568       quantities.push_back( nbNodes );
4569
4570       // top of prism
4571       polyedre_nodes.push_back( nextNod[0] );
4572       for (int inode = nbNodes; inode-1; --inode )
4573         polyedre_nodes.push_back( nextNod[inode-1] );
4574       quantities.push_back( nbNodes );
4575
4576       // side faces
4577       // 3--6--2
4578       // |     |
4579       // 7     5
4580       // |     |
4581       // 0--4--1
4582       const int iQuad = elem->IsQuadratic();
4583       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4584       {
4585         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4586         int inextface = (iface+1+iQuad) % nbNodes;
4587         int imid      = (iface+1) % nbNodes;
4588         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4589         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4590         polyedre_nodes.push_back( prevNod[iface] );             // 1
4591         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4592         {
4593           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4594           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4595         }
4596         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4597         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4598         {
4599           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4600           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4601         }
4602         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4603         if ( nbFaceNodes > 2 )
4604           quantities.push_back( nbFaceNodes );
4605         else // degenerated face
4606           polyedre_nodes.resize( prevNbNodes );
4607       }
4608       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4609
4610     } // try to create a polyherdal prism
4611
4612     if ( aNewElem ) {
4613       newElems.push_back( aNewElem );
4614       myLastCreatedElems.push_back(aNewElem);
4615       srcElements.push_back( elem );
4616     }
4617
4618     // set new prev nodes
4619     for ( iNode = 0; iNode < nbNodes; iNode++ )
4620       prevNod[ iNode ] = nextNod[ iNode ];
4621
4622   } // loop on steps
4623 }
4624
4625 //=======================================================================
4626 /*!
4627  * \brief Create 1D and 2D elements around swept elements
4628  * \param mapNewNodes - source nodes and ones generated from them
4629  * \param newElemsMap - source elements and ones generated from them
4630  * \param elemNewNodesMap - nodes generated from each node of each element
4631  * \param elemSet - all swept elements
4632  * \param nbSteps - number of sweeping steps
4633  * \param srcElements - to append elem for each generated element
4634  */
4635 //=======================================================================
4636
4637 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4638                                   TTElemOfElemListMap &    newElemsMap,
4639                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4640                                   TIDSortedElemSet&        elemSet,
4641                                   const int                nbSteps,
4642                                   SMESH_SequenceOfElemPtr& srcElements)
4643 {
4644   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4645   SMESHDS_Mesh* aMesh = GetMeshDS();
4646
4647   // Find nodes belonging to only one initial element - sweep them into edges.
4648
4649   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4650   for ( ; nList != mapNewNodes.end(); nList++ )
4651   {
4652     const SMDS_MeshNode* node =
4653       static_cast<const SMDS_MeshNode*>( nList->first );
4654     if ( newElemsMap.count( node ))
4655       continue; // node was extruded into edge
4656     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4657     int nbInitElems = 0;
4658     const SMDS_MeshElement* el = 0;
4659     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4660     while ( eIt->more() && nbInitElems < 2 ) {
4661       const SMDS_MeshElement* e = eIt->next();
4662       SMDSAbs_ElementType  type = e->GetType();
4663       if ( type == SMDSAbs_Volume ||
4664            type < highType ||
4665            !elemSet.count(e))
4666         continue;
4667       if ( type > highType ) {
4668         nbInitElems = 0;
4669         highType    = type;
4670       }
4671       el = e;
4672       ++nbInitElems;
4673     }
4674     if ( nbInitElems == 1 ) {
4675       bool NotCreateEdge = el && el->IsMediumNode(node);
4676       if(!NotCreateEdge) {
4677         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4678         list<const SMDS_MeshElement*> newEdges;
4679         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4680       }
4681     }
4682   }
4683
4684   // Make a ceiling for each element ie an equal element of last new nodes.
4685   // Find free links of faces - make edges and sweep them into faces.
4686
4687   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4688
4689   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4690   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4691   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4692   {
4693     const SMDS_MeshElement* elem = itElem->first;
4694     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4695
4696     if(itElem->second.size()==0) continue;
4697
4698     const bool isQuadratic = elem->IsQuadratic();
4699
4700     if ( elem->GetType() == SMDSAbs_Edge ) {
4701       // create a ceiling edge
4702       if ( !isQuadratic ) {
4703         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4704                                vecNewNodes[ 1 ]->second.back())) {
4705           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4706                                                       vecNewNodes[ 1 ]->second.back()));
4707           srcElements.push_back( elem );
4708         }
4709       }
4710       else {
4711         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4712                                vecNewNodes[ 1 ]->second.back(),
4713                                vecNewNodes[ 2 ]->second.back())) {
4714           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4715                                                       vecNewNodes[ 1 ]->second.back(),
4716                                                       vecNewNodes[ 2 ]->second.back()));
4717           srcElements.push_back( elem );
4718         }
4719       }
4720     }
4721     if ( elem->GetType() != SMDSAbs_Face )
4722       continue;
4723
4724     bool hasFreeLinks = false;
4725
4726     TIDSortedElemSet avoidSet;
4727     avoidSet.insert( elem );
4728
4729     set<const SMDS_MeshNode*> aFaceLastNodes;
4730     int iNode, nbNodes = vecNewNodes.size();
4731     if ( !isQuadratic ) {
4732       // loop on the face nodes
4733       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4734         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735         // look for free links of the face
4736         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4737         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4738         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4739         // check if a link n1-n2 is free
4740         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4741           hasFreeLinks = true;
4742           // make a new edge and a ceiling for a new edge
4743           const SMDS_MeshElement* edge;
4744           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4745             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4746             srcElements.push_back( myLastCreatedElems.back() );
4747           }
4748           n1 = vecNewNodes[ iNode ]->second.back();
4749           n2 = vecNewNodes[ iNext ]->second.back();
4750           if ( !aMesh->FindEdge( n1, n2 )) {
4751             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4752             srcElements.push_back( edge );
4753           }
4754         }
4755       }
4756     }
4757     else { // elem is quadratic face
4758       int nbn = nbNodes/2;
4759       for ( iNode = 0; iNode < nbn; iNode++ ) {
4760         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4762         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4763         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4764         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4765         // check if a link is free
4766         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4767              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4768              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4769           hasFreeLinks = true;
4770           // make an edge and a ceiling for a new edge
4771           // find medium node
4772           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4773             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4774             srcElements.push_back( elem );
4775           }
4776           n1 = vecNewNodes[ iNode ]->second.back();
4777           n2 = vecNewNodes[ iNext ]->second.back();
4778           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4779           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4780             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4781             srcElements.push_back( elem );
4782           }
4783         }
4784       }
4785       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4786         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4787       }
4788     }
4789
4790     // sweep free links into faces
4791
4792     if ( hasFreeLinks ) {
4793       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4794       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4795
4796       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4797       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4798       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4799         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4800         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4801       }
4802       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4803         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4804         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4805       }
4806       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4807         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4808         std::advance( v, volNb );
4809         // find indices of free faces of a volume and their source edges
4810         list< int > freeInd;
4811         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4812         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4813         int iF, nbF = vTool.NbFaces();
4814         for ( iF = 0; iF < nbF; iF ++ ) {
4815           if ( vTool.IsFreeFace( iF ) &&
4816                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4817                initNodeSet != faceNodeSet) // except an initial face
4818           {
4819             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4820               continue;
4821             if ( faceNodeSet == initNodeSetNoCenter )
4822               continue;
4823             freeInd.push_back( iF );
4824             // find source edge of a free face iF
4825             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4826             vector<const SMDS_MeshNode*>::iterator lastCommom;
4827             commonNodes.resize( nbNodes, 0 );
4828             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4829                                                 initNodeSet.begin(), initNodeSet.end(),
4830                                                 commonNodes.begin());
4831             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4832               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4833             else
4834               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4835 #ifdef _DEBUG_
4836             if ( !srcEdges.back() )
4837             {
4838               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4839                    << iF << " of volume #" << vTool.ID() << endl;
4840             }
4841 #endif
4842           }
4843         }
4844         if ( freeInd.empty() )
4845           continue;
4846
4847         // create wall faces for all steps;
4848         // if such a face has been already created by sweep of edge,
4849         // assure that its orientation is OK
4850         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4851         {
4852           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4853           vTool.SetExternalNormal();
4854           const int nextShift = vTool.IsForward() ? +1 : -1;
4855           list< int >::iterator ind = freeInd.begin();
4856           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4857           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4858           {
4859             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4860             int nbn = vTool.NbFaceNodes( *ind );
4861             const SMDS_MeshElement * f = 0;
4862             if ( nbn == 3 )              ///// triangle
4863             {
4864               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4865               if ( !f ||
4866                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4867               {
4868                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4869                                                      nodes[ 1 ],
4870                                                      nodes[ 1 + nextShift ] };
4871                 if ( f )
4872                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4873                 else
4874                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4875                                                                newOrder[ 2 ] ));
4876               }
4877             }
4878             else if ( nbn == 4 )       ///// quadrangle
4879             {
4880               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4881               if ( !f ||
4882                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4883               {
4884                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4885                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4886                 if ( f )
4887                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4888                 else
4889                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4890                                                                newOrder[ 2 ], newOrder[ 3 ]));
4891               }
4892             }
4893             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4894             {
4895               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4896               if ( !f ||
4897                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4898               {
4899                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4900                                                      nodes[2],
4901                                                      nodes[2 + 2*nextShift],
4902                                                      nodes[3 - 2*nextShift],
4903                                                      nodes[3],
4904                                                      nodes[3 + 2*nextShift]};
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4909                                                                newOrder[ 1 ],
4910                                                                newOrder[ 2 ],
4911                                                                newOrder[ 3 ],
4912                                                                newOrder[ 4 ],
4913                                                                newOrder[ 5 ] ));
4914               }
4915             }
4916             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4917             {
4918               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4919                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4920               if ( !f ||
4921                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4922               {
4923                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4924                                                      nodes[4 - 2*nextShift],
4925                                                      nodes[4],
4926                                                      nodes[4 + 2*nextShift],
4927                                                      nodes[1],
4928                                                      nodes[5 - 2*nextShift],
4929                                                      nodes[5],
4930                                                      nodes[5 + 2*nextShift] };
4931                 if ( f )
4932                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4933                 else
4934                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4935                                                               newOrder[ 2 ], newOrder[ 3 ],
4936                                                               newOrder[ 4 ], newOrder[ 5 ],
4937                                                               newOrder[ 6 ], newOrder[ 7 ]));
4938               }
4939             }
4940             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4941             {
4942               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4943                                       SMDSAbs_Face, /*noMedium=*/false);
4944               if ( !f ||
4945                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4946               {
4947                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4948                                                      nodes[4 - 2*nextShift],
4949                                                      nodes[4],
4950                                                      nodes[4 + 2*nextShift],
4951                                                      nodes[1],
4952                                                      nodes[5 - 2*nextShift],
4953                                                      nodes[5],
4954                                                      nodes[5 + 2*nextShift],
4955                                                      nodes[8] };
4956                 if ( f )
4957                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4958                 else
4959                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4960                                                               newOrder[ 2 ], newOrder[ 3 ],
4961                                                               newOrder[ 4 ], newOrder[ 5 ],
4962                                                               newOrder[ 6 ], newOrder[ 7 ],
4963                                                               newOrder[ 8 ]));
4964               }
4965             }
4966             else  //////// polygon
4967             {
4968               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4969               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4970               if ( !f ||
4971                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4972               {
4973                 if ( !vTool.IsForward() )
4974                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4975                 if ( f )
4976                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4977                 else
4978                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4979               }
4980             }
4981
4982             while ( srcElements.size() < myLastCreatedElems.size() )
4983               srcElements.push_back( *srcEdge );
4984
4985           }  // loop on free faces
4986
4987           // go to the next volume
4988           iVol = 0;
4989           while ( iVol++ < nbVolumesByStep ) v++;
4990
4991         } // loop on steps
4992       } // loop on volumes of one step
4993     } // sweep free links into faces
4994
4995     // Make a ceiling face with a normal external to a volume
4996
4997     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4998     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4999     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5000
5001     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5002       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5003       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5004     }
5005     if ( iF >= 0 )
5006     {
5007       lastVol.SetExternalNormal();
5008       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5009       const               int nbn = lastVol.NbFaceNodes( iF );
5010       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5011       if ( !hasFreeLinks ||
5012            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5013       {
5014         const vector<int>& interlace =
5015           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5016         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5017
5018         AddElement( nodeVec, anyFace.Init( elem ));
5019
5020         while ( srcElements.size() < myLastCreatedElems.size() )
5021           srcElements.push_back( elem );
5022       }
5023     }
5024   } // loop on swept elements
5025 }
5026
5027 //=======================================================================
5028 //function : RotationSweep
5029 //purpose  :
5030 //=======================================================================
5031
5032 SMESH_MeshEditor::PGroupIDs
5033 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5034                                 const gp_Ax1&      theAxis,
5035                                 const double       theAngle,
5036                                 const int          theNbSteps,
5037                                 const double       theTol,
5038                                 const bool         theMakeGroups,
5039                                 const bool         theMakeWalls)
5040 {
5041   ClearLastCreated();
5042
5043   setElemsFirst( theElemSets );
5044   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5045   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5046
5047   // source elements for each generated one
5048   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5049   srcElems.reserve( theElemSets[0].size() );
5050   srcNodes.reserve( theElemSets[1].size() );
5051
5052   gp_Trsf aTrsf;
5053   aTrsf.SetRotation( theAxis, theAngle );
5054   gp_Trsf aTrsf2;
5055   aTrsf2.SetRotation( theAxis, theAngle/2. );
5056
5057   gp_Lin aLine( theAxis );
5058   double aSqTol = theTol * theTol;
5059
5060   SMESHDS_Mesh* aMesh = GetMeshDS();
5061
5062   TNodeOfNodeListMap mapNewNodes;
5063   TElemOfVecOfNnlmiMap mapElemNewNodes;
5064   TTElemOfElemListMap newElemsMap;
5065
5066   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5067                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5068                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5069   // loop on theElemSets
5070   TIDSortedElemSet::iterator itElem;
5071   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5072   {
5073     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5074     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5075       const SMDS_MeshElement* elem = *itElem;
5076       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5077         continue;
5078       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5079       newNodesItVec.reserve( elem->NbNodes() );
5080
5081       // loop on elem nodes
5082       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5083       while ( itN->more() )
5084       {
5085         const SMDS_MeshNode* node = cast2Node( itN->next() );
5086
5087         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5088         double coord[3];
5089         aXYZ.Coord( coord[0], coord[1], coord[2] );
5090         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5091
5092         // check if a node has been already sweeped
5093         TNodeOfNodeListMapItr nIt =
5094           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5095         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5096         if ( listNewNodes.empty() )
5097         {
5098           // check if we are to create medium nodes between corner ones
5099           bool needMediumNodes = false;
5100           if ( isQuadraticMesh )
5101           {
5102             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5103             while (it->more() && !needMediumNodes )
5104             {
5105               const SMDS_MeshElement* invElem = it->next();
5106               if ( invElem != elem && !theElems.count( invElem )) continue;
5107               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5108               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5109                 needMediumNodes = true;
5110             }
5111           }
5112
5113           // make new nodes
5114           const SMDS_MeshNode * newNode = node;
5115           for ( int i = 0; i < theNbSteps; i++ ) {
5116             if ( !isOnAxis ) {
5117               if ( needMediumNodes )  // create a medium node
5118               {
5119                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5120                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5121                 myLastCreatedNodes.push_back(newNode);
5122                 srcNodes.push_back( node );
5123                 listNewNodes.push_back( newNode );
5124                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5125               }
5126               else {
5127                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5128               }
5129               // create a corner node
5130               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5131               myLastCreatedNodes.push_back(newNode);
5132               srcNodes.push_back( node );
5133               listNewNodes.push_back( newNode );
5134             }
5135             else {
5136               listNewNodes.push_back( newNode );
5137               // if ( needMediumNodes )
5138               //   listNewNodes.push_back( newNode );
5139             }
5140           }
5141         }
5142         newNodesItVec.push_back( nIt );
5143       }
5144       // make new elements
5145       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5146     }
5147   }
5148
5149   if ( theMakeWalls )
5150     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5151
5152   PGroupIDs newGroupIDs;
5153   if ( theMakeGroups )
5154     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5155
5156   return newGroupIDs;
5157 }
5158
5159 //=======================================================================
5160 //function : ExtrusParam
5161 //purpose  : standard construction
5162 //=======================================================================
5163
5164 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5165                                             const int                theNbSteps,
5166                                             const std::list<double>& theScales,
5167                                             const std::list<double>& theAngles,
5168                                             const gp_XYZ*            theBasePoint,
5169                                             const int                theFlags,
5170                                             const double             theTolerance):
5171   myDir( theStep ),
5172   myBaseP( Precision::Infinite(), 0, 0 ),
5173   myFlags( theFlags ),
5174   myTolerance( theTolerance ),
5175   myElemsToUse( NULL )
5176 {
5177   mySteps = new TColStd_HSequenceOfReal;
5178   const double stepSize = theStep.Magnitude();
5179   for (int i=1; i<=theNbSteps; i++ )
5180     mySteps->Append( stepSize );
5181
5182   if ( !theScales.empty() )
5183   {
5184     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5185       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5186
5187     // add medium scales
5188     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5189     myScales.reserve( theNbSteps * 2 );
5190     myScales.push_back( 0.5 * ( *s1 + 1. ));
5191     myScales.push_back( *s1 );
5192     for ( ; s2 != theScales.end(); s1 = s2++ )
5193     {
5194       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5195       myScales.push_back( *s2 );
5196     }
5197   }
5198
5199   if ( !theAngles.empty() )
5200   {
5201     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5202     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5203       linearAngleVariation( theNbSteps, angles );
5204
5205     // accumulate angles
5206     double angle = 0;
5207     int nbAngles = 0;
5208     std::list<double>::iterator a1 = angles.begin(), a2;
5209     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5210     {
5211       angle += *a1;
5212       *a1 = angle;
5213     }
5214     while ( nbAngles++ < theNbSteps )
5215       angles.push_back( angles.back() );
5216
5217     // add medium angles
5218     a2 = angles.begin(), a1 = a2++;
5219     myAngles.push_back( 0.5 * *a1 );
5220     myAngles.push_back( *a1 );
5221     for ( ; a2 != angles.end(); a1 = a2++ )
5222     {
5223       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5224       myAngles.push_back( *a2 );
5225     }
5226   }
5227
5228   if ( theBasePoint )
5229   {
5230     myBaseP = *theBasePoint;
5231   }
5232
5233   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5234       ( theTolerance > 0 ))
5235   {
5236     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5237   }
5238   else
5239   {
5240     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5241   }
5242 }
5243
5244 //=======================================================================
5245 //function : ExtrusParam
5246 //purpose  : steps are given explicitly
5247 //=======================================================================
5248
5249 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5250                                             Handle(TColStd_HSequenceOfReal) theSteps,
5251                                             const int                       theFlags,
5252                                             const double                    theTolerance):
5253   myDir( theDir ),
5254   mySteps( theSteps ),
5255   myFlags( theFlags ),
5256   myTolerance( theTolerance ),
5257   myElemsToUse( NULL )
5258 {
5259   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5260       ( theTolerance > 0 ))
5261   {
5262     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5263   }
5264   else
5265   {
5266     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5267   }
5268 }
5269
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose  : for extrusion by normal
5273 //=======================================================================
5274
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5276                                             const int    theNbSteps,
5277                                             const int    theFlags,
5278                                             const int    theDim ):
5279   myDir( 1,0,0 ),
5280   mySteps( new TColStd_HSequenceOfReal ),
5281   myFlags( theFlags ),
5282   myTolerance( 0 ),
5283   myElemsToUse( NULL )
5284 {
5285   for (int i = 0; i < theNbSteps; i++ )
5286     mySteps->Append( theStepSize );
5287
5288   if ( theDim == 1 )
5289   {
5290     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5291   }
5292   else
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5295   }
5296 }
5297
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose  : for extrusion along path
5301 //=======================================================================
5302
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5304                                             const gp_Pnt*                   theBasePoint,
5305                                             const std::list<double>&        theScales,
5306                                             const bool                      theMakeGroups )
5307   : myBaseP( Precision::Infinite(), 0, 0 ),
5308     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5309     myPathPoints( thePoints )
5310 {
5311   if ( theBasePoint )
5312   {
5313     myBaseP = theBasePoint->XYZ();
5314   }
5315
5316   if ( !theScales.empty() )
5317   {
5318     // add medium scales
5319     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5320     myScales.reserve( thePoints.size() * 2 );
5321     myScales.push_back( 0.5 * ( 1. + *s1 ));
5322     myScales.push_back( *s1 );
5323     for ( ; s2 != theScales.end(); s1 = s2++ )
5324     {
5325       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5326       myScales.push_back( *s2 );
5327     }
5328   }
5329
5330   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5331 }
5332
5333 //=======================================================================
5334 //function : ExtrusParam::SetElementsToUse
5335 //purpose  : stores elements to use for extrusion by normal, depending on
5336 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5337 //           define myBaseP for scaling
5338 //=======================================================================
5339
5340 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5341                                                       const TIDSortedElemSet& nodes )
5342 {
5343   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5344
5345   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5346   {
5347     myBaseP.SetCoord( 0.,0.,0. );
5348     TIDSortedElemSet newNodes;
5349
5350     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5351     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5352     {
5353       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5354       TIDSortedElemSet::const_iterator itElem = elements.begin();
5355       for ( ; itElem != elements.end(); itElem++ )
5356       {
5357         const SMDS_MeshElement* elem = *itElem;
5358         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5359         while ( itN->more() ) {
5360           const SMDS_MeshElement* node = itN->next();
5361           if ( newNodes.insert( node ).second )
5362             myBaseP += SMESH_NodeXYZ( node );
5363         }
5364       }
5365     }
5366     myBaseP /= newNodes.size();
5367   }
5368 }
5369
5370 //=======================================================================
5371 //function : ExtrusParam::beginStepIter
5372 //purpose  : prepare iteration on steps
5373 //=======================================================================
5374
5375 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5376 {
5377   myWithMediumNodes = withMediumNodes;
5378   myNextStep = 1;
5379   myCurSteps.clear();
5380 }
5381 //=======================================================================
5382 //function : ExtrusParam::moreSteps
5383 //purpose  : are there more steps?
5384 //=======================================================================
5385
5386 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5387 {
5388   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5389 }
5390 //=======================================================================
5391 //function : ExtrusParam::nextStep
5392 //purpose  : returns the next step
5393 //=======================================================================
5394
5395 double SMESH_MeshEditor::ExtrusParam::nextStep()
5396 {
5397   double res = 0;
5398   if ( !myCurSteps.empty() )
5399   {
5400     res = myCurSteps.back();
5401     myCurSteps.pop_back();
5402   }
5403   else if ( myNextStep <= mySteps->Length() )
5404   {
5405     myCurSteps.push_back( mySteps->Value( myNextStep ));
5406     ++myNextStep;
5407     if ( myWithMediumNodes )
5408     {
5409       myCurSteps.back() /= 2.;
5410       myCurSteps.push_back( myCurSteps.back() );
5411     }
5412     res = nextStep();
5413   }
5414   return res;
5415 }
5416
5417 //=======================================================================
5418 //function : ExtrusParam::makeNodesByDir
5419 //purpose  : create nodes for standard extrusion
5420 //=======================================================================
5421
5422 int SMESH_MeshEditor::ExtrusParam::
5423 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5424                 const SMDS_MeshNode*              srcNode,
5425                 std::list<const SMDS_MeshNode*> & newNodes,
5426                 const bool                        makeMediumNodes)
5427 {
5428   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5429
5430   int nbNodes = 0;
5431   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5432   {
5433     p += myDir.XYZ() * nextStep();
5434     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5435     newNodes.push_back( newNode );
5436   }
5437
5438   if ( !myScales.empty() || !myAngles.empty() )
5439   {
5440     gp_XYZ  center = myBaseP;
5441     gp_Ax1  ratationAxis( center, myDir );
5442     gp_Trsf rotation;
5443
5444     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5445     size_t i = !makeMediumNodes;
5446     for ( beginStepIter( makeMediumNodes );
5447           moreSteps();
5448           ++nIt, i += 1 + !makeMediumNodes )
5449     {
5450       center += myDir.XYZ() * nextStep();
5451
5452       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5453       bool moved = false;
5454       if ( i < myScales.size() )
5455       {
5456         xyz = ( myScales[i] * ( xyz - center )) + center;
5457         moved = true;
5458       }
5459       if ( !myAngles.empty() )
5460       {
5461         rotation.SetRotation( ratationAxis, myAngles[i] );
5462         rotation.Transforms( xyz );
5463         moved = true;
5464       }
5465       if ( moved )
5466         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5467       else
5468         break;
5469     }
5470   }
5471   return nbNodes;
5472 }
5473
5474 //=======================================================================
5475 //function : ExtrusParam::makeNodesByDirAndSew
5476 //purpose  : create nodes for standard extrusion with sewing
5477 //=======================================================================
5478
5479 int SMESH_MeshEditor::ExtrusParam::
5480 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5481                       const SMDS_MeshNode*              srcNode,
5482                       std::list<const SMDS_MeshNode*> & newNodes,
5483                       const bool                        makeMediumNodes)
5484 {
5485   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5486
5487   int nbNodes = 0;
5488   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5489   {
5490     P1 += myDir.XYZ() * nextStep();
5491
5492     // try to search in sequence of existing nodes
5493     // if myNodes.size()>0 we 'nave to use given sequence
5494     // else - use all nodes of mesh
5495     const SMDS_MeshNode * node = 0;
5496     if ( myNodes.Length() > 0 )
5497     {
5498       for ( int i = 1; i <= myNodes.Length(); i++ )
5499       {
5500         SMESH_NodeXYZ P2 = myNodes.Value(i);
5501         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5502         {
5503           node = myNodes.Value(i);
5504           break;
5505         }
5506       }
5507     }
5508     else
5509     {
5510       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5511       while(itn->more())
5512       {
5513         SMESH_NodeXYZ P2 = itn->next();
5514         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5515         {
5516           node = P2._node;
5517           break;
5518         }
5519       }
5520     }
5521
5522     if ( !node )
5523       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5524
5525     newNodes.push_back( node );
5526
5527   } // loop on steps
5528
5529   return nbNodes;
5530 }
5531
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByNormal2D
5534 //purpose  : create nodes for extrusion using normals of faces
5535 //=======================================================================
5536
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5539                      const SMDS_MeshNode*              srcNode,
5540                      std::list<const SMDS_MeshNode*> & newNodes,
5541                      const bool                        makeMediumNodes)
5542 {
5543   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5544
5545   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5546
5547   // get normals to faces sharing srcNode
5548   vector< gp_XYZ > norms, baryCenters;
5549   gp_XYZ norm, avgNorm( 0,0,0 );
5550   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5551   while ( faceIt->more() )
5552   {
5553     const SMDS_MeshElement* face = faceIt->next();
5554     if ( myElemsToUse && !myElemsToUse->count( face ))
5555       continue;
5556     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5557     {
5558       norms.push_back( norm );
5559       avgNorm += norm;
5560       if ( !alongAvgNorm )
5561       {
5562         gp_XYZ bc(0,0,0);
5563         int nbN = 0;
5564         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5565           bc += SMESH_NodeXYZ( nIt->next() );
5566         baryCenters.push_back( bc / nbN );
5567       }
5568     }
5569   }
5570
5571   if ( norms.empty() ) return 0;
5572
5573   double normSize = avgNorm.Modulus();
5574   if ( normSize < std::numeric_limits<double>::min() )
5575     return 0;
5576
5577   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5578   {
5579     myDir = avgNorm;
5580     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5581   }
5582
5583   avgNorm /= normSize;
5584
5585   int nbNodes = 0;
5586   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5587   {
5588     gp_XYZ pNew = p;
5589     double stepSize = nextStep();
5590
5591     if ( norms.size() > 1 )
5592     {
5593       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5594       {
5595         // translate plane of a face
5596         baryCenters[ iF ] += norms[ iF ] * stepSize;
5597
5598         // find point of intersection of the face plane located at baryCenters[ iF ]
5599         // and avgNorm located at pNew
5600         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5601         double dot  = ( norms[ iF ] * avgNorm );
5602         if ( dot < std::numeric_limits<double>::min() )
5603           dot = stepSize * 1e-3;
5604         double step = -( norms[ iF ] * pNew + d ) / dot;
5605         pNew += step * avgNorm;
5606       }
5607     }
5608     else
5609     {
5610       pNew += stepSize * avgNorm;
5611     }
5612     p = pNew;
5613
5614     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5615     newNodes.push_back( newNode );
5616   }
5617   return nbNodes;
5618 }
5619
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByNormal1D
5622 //purpose  : create nodes for extrusion using normals of edges
5623 //=======================================================================
5624
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5627                      const SMDS_MeshNode*              srcNode,
5628                      std::list<const SMDS_MeshNode*> & newNodes,
5629                      const bool                        makeMediumNodes)
5630 {
5631   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5632   return 0;
5633 }
5634
5635 //=======================================================================
5636 //function : ExtrusParam::makeNodesAlongTrack
5637 //purpose  : create nodes for extrusion along path
5638 //=======================================================================
5639
5640 int SMESH_MeshEditor::ExtrusParam::
5641 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5642                      const SMDS_MeshNode*              srcNode,
5643                      std::list<const SMDS_MeshNode*> & newNodes,
5644                      const bool                        makeMediumNodes)
5645 {
5646   const Standard_Real aTolAng=1.e-4;
5647
5648   gp_Pnt aV0x = myBaseP;
5649   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5650
5651   const PathPoint& aPP0 = myPathPoints[0];
5652   gp_Pnt aP0x = aPP0.myPnt;
5653   gp_Dir aDT0x= aPP0.myTgt;
5654
5655   std::vector< gp_Pnt > centers;
5656   centers.reserve( NbSteps() * 2 );
5657
5658   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5659
5660   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5661   {
5662     const PathPoint&  aPP  = myPathPoints[j];
5663     const gp_Pnt&     aP1x = aPP.myPnt;
5664     const gp_Dir&    aDT1x = aPP.myTgt;
5665
5666     // Translation
5667     gp_Vec aV01x( aP0x, aP1x );
5668     aTrsf.SetTranslation( aV01x );
5669     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5670     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5671
5672     // rotation 1 [ T1,T0 ]
5673     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5674     if ( fabs( aAngleT1T0 ) > aTolAng )
5675     {
5676       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5677       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5678
5679       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5680     }
5681
5682     // rotation 2
5683     if ( aPP.myAngle != 0. )
5684     {
5685       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5686       aPN1 = aPN1.Transformed( aTrsfRot );
5687     }
5688
5689     // make new node
5690     if ( makeMediumNodes )
5691     {
5692       // create additional node
5693       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5694       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5695       newNodes.push_back( newNode );
5696
5697     }
5698     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5699     newNodes.push_back( newNode );
5700
5701     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5702     centers.push_back( aV1x );
5703
5704     aPN0 = aPN1;
5705     aP0x = aP1x;
5706     aV0x = aV1x;
5707     aDT0x = aDT1x;
5708   }
5709
5710   // scale
5711   if ( !myScales.empty() )
5712   {
5713     gp_Trsf aTrsfScale;
5714     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5715     for ( size_t i = !makeMediumNodes;
5716           i < myScales.size() && node != newNodes.end();
5717           i += ( 1 + !makeMediumNodes ), ++node )
5718     {
5719       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5720       gp_Pnt aN = SMESH_NodeXYZ( *node );
5721       gp_Pnt aP = aN.Transformed( aTrsfScale );
5722       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5723     }
5724   }
5725
5726   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5727 }
5728
5729 //=======================================================================
5730 //function : ExtrusionSweep
5731 //purpose  :
5732 //=======================================================================
5733
5734 SMESH_MeshEditor::PGroupIDs
5735 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5736                                   const gp_Vec&        theStep,
5737                                   const int            theNbSteps,
5738                                   TTElemOfElemListMap& newElemsMap,
5739                                   const int            theFlags,
5740                                   const double         theTolerance)
5741 {
5742   std::list<double> dummy;
5743   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5744                        theFlags, theTolerance );
5745   return ExtrusionSweep( theElems, aParams, newElemsMap );
5746 }
5747
5748 namespace
5749 {
5750
5751 //=======================================================================
5752 //function : getOriFactor
5753 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5754 //           edge curve orientation
5755 //=======================================================================
5756
5757   double getOriFactor( const TopoDS_Edge&   edge,
5758                        const SMDS_MeshNode* n1,
5759                        const SMDS_MeshNode* n2,
5760                        SMESH_MesherHelper&  helper)
5761   {
5762     double u1 = helper.GetNodeU( edge, n1, n2 );
5763     double u2 = helper.GetNodeU( edge, n2, n1 );
5764     return u1 < u2 ? 1. : -1.;
5765   }
5766 }
5767
5768 //=======================================================================
5769 //function : ExtrusionSweep
5770 //purpose  :
5771 //=======================================================================
5772
5773 SMESH_MeshEditor::PGroupIDs
5774 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5775                                   ExtrusParam&         theParams,
5776                                   TTElemOfElemListMap& newElemsMap)
5777 {
5778   ClearLastCreated();
5779
5780   setElemsFirst( theElemSets );
5781   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5782   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5783
5784   // source elements for each generated one
5785   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5786   srcElems.reserve( theElemSets[0].size() );
5787   srcNodes.reserve( theElemSets[1].size() );
5788
5789   const int nbSteps = theParams.NbSteps();
5790   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5791
5792   TNodeOfNodeListMap   mapNewNodes;
5793   TElemOfVecOfNnlmiMap mapElemNewNodes;
5794
5795   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5796                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5797                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5798   // loop on theElems
5799   TIDSortedElemSet::iterator itElem;
5800   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5801   {
5802     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5803     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5804     {
5805       // check element type
5806       const SMDS_MeshElement* elem = *itElem;
5807       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5808         continue;
5809
5810       const size_t nbNodes = elem->NbNodes();
5811       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5812       newNodesItVec.reserve( nbNodes );
5813
5814       // loop on elem nodes
5815       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5816       while ( itN->more() )
5817       {
5818         // check if a node has been already sweeped
5819         const SMDS_MeshNode* node = itN->next();
5820         TNodeOfNodeListMap::iterator nIt =
5821           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5822         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5823         if ( listNewNodes.empty() )
5824         {
5825           // make new nodes
5826
5827           // check if we are to create medium nodes between corner ones
5828           bool needMediumNodes = false;
5829           if ( isQuadraticMesh )
5830           {
5831             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5832             while (it->more() && !needMediumNodes )
5833             {
5834               const SMDS_MeshElement* invElem = it->next();
5835               if ( invElem != elem && !theElems.count( invElem )) continue;
5836               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5837               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5838                 needMediumNodes = true;
5839             }
5840           }
5841           // create nodes for all steps
5842           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5843           {
5844             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5845             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5846             {
5847               myLastCreatedNodes.push_back( *newNodesIt );
5848               srcNodes.push_back( node );
5849             }
5850           }
5851           else
5852           {
5853             if ( theParams.ToMakeBoundary() )
5854             {
5855               GetMeshDS()->Modified();
5856               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5857             }
5858             break; // newNodesItVec will be shorter than nbNodes
5859           }
5860         }
5861         newNodesItVec.push_back( nIt );
5862       }
5863       // make new elements
5864       if ( newNodesItVec.size() == nbNodes )
5865         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5866     }
5867   }
5868
5869   if ( theParams.ToMakeBoundary() ) {
5870     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5871   }
5872   PGroupIDs newGroupIDs;
5873   if ( theParams.ToMakeGroups() )
5874     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5875
5876   return newGroupIDs;
5877 }
5878
5879 //=======================================================================
5880 //function : ExtrusionAlongTrack
5881 //purpose  :
5882 //=======================================================================
5883 SMESH_MeshEditor::Extrusion_Error
5884 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5885                                        SMESH_Mesh*          theTrackMesh,
5886                                        SMDS_ElemIteratorPtr theTrackIterator,
5887                                        const SMDS_MeshNode* theN1,
5888                                        std::list<double>&   theAngles,
5889                                        const bool           theAngleVariation,
5890                                        std::list<double>&   theScales,
5891                                        const bool           theScaleVariation,
5892                                        const gp_Pnt*        theRefPoint,
5893                                        const bool           theMakeGroups)
5894 {
5895   ClearLastCreated();
5896
5897   // 1. Check data
5898   if ( theElements[0].empty() && theElements[1].empty() )
5899     return EXTR_NO_ELEMENTS;
5900
5901   ASSERT( theTrackMesh );
5902   if ( ! theTrackIterator || !theTrackIterator->more() )
5903     return EXTR_NO_ELEMENTS;
5904
5905   // 2. Get ordered nodes
5906   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5907   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5908   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5909   if ( branchEdges.empty() )
5910     return EXTR_PATH_NOT_EDGE;
5911
5912   if ( branchEdges.size() > 1 )
5913     return EXTR_BAD_PATH_SHAPE;
5914
5915   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5916   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5917   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5918     return EXTR_BAD_STARTING_NODE;
5919
5920   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5921   {
5922     // add medium nodes to pathNodes
5923     std::vector< const SMDS_MeshNode* >    pathNodes2;
5924     std::vector< const SMDS_MeshElement* > pathEdges2;
5925     pathNodes2.reserve( pathNodes.size() * 2 );
5926     pathEdges2.reserve( pathEdges.size() * 2 );
5927     for ( size_t i = 0; i < pathEdges.size(); ++i )
5928     {
5929       pathNodes2.push_back( pathNodes[i] );
5930       pathEdges2.push_back( pathEdges[i] );
5931       if ( pathEdges[i]->IsQuadratic() )
5932       {
5933         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5934         pathEdges2.push_back( pathEdges[i] );
5935       }
5936     }
5937     pathNodes2.push_back( pathNodes.back() );
5938     pathEdges.swap( pathEdges2 );
5939     pathNodes.swap( pathNodes2 );
5940   }
5941
5942   // 3. Get path data at pathNodes
5943
5944   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5945
5946   if ( theAngleVariation )
5947     linearAngleVariation( points.size()-1, theAngles );
5948   if ( theScaleVariation )
5949     linearScaleVariation( points.size()-1, theScales );
5950
5951   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5952   std::list<double>::iterator angle = theAngles.begin();
5953
5954   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5955
5956   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5957   std::map< int, double >::iterator id2factor;
5958   SMESH_MesherHelper pathHelper( *theTrackMesh );
5959   gp_Pnt p; gp_Vec tangent;
5960   const double tol2 = gp::Resolution() * gp::Resolution();
5961
5962   for ( size_t i = 0; i < pathNodes.size(); ++i )
5963   {
5964     ExtrusParam::PathPoint & point = points[ i ];
5965
5966     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5967
5968     if ( angle != theAngles.end() )
5969       point.myAngle = *angle++;
5970
5971     tangent.SetCoord( 0,0,0 );
5972     const int          shapeID = pathNodes[ i ]->GetShapeID();
5973     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
5974     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5975     switch ( shapeType )
5976     {
5977     case TopAbs_EDGE:
5978     {
5979       TopoDS_Edge edge = TopoDS::Edge( shape );
5980       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5981       if ( id2factor->second == 0 )
5982       {
5983         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
5984         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
5985       }
5986       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
5987       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
5988       curve->D1( u, p, tangent );
5989       tangent *= id2factor->second;
5990       break;
5991     }
5992     case TopAbs_VERTEX:
5993     {
5994       int nbEdges = 0;
5995       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
5996       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
5997       {
5998         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
5999         for ( int di = -1; di <= 0; ++di )
6000         {
6001           size_t j = i + di;
6002           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6003           {
6004             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6005             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6006             if ( id2factor->second == 0 )
6007             {
6008               if ( j < i )
6009                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6010               else
6011                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6012             }
6013             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6014             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6015             gp_Vec du;
6016             curve->D1( u, p, du );
6017             double size2 = du.SquareMagnitude();
6018             if ( du.SquareMagnitude() > tol2 )
6019             {
6020               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6021               nbEdges++;
6022             }
6023             break;
6024           }
6025         }
6026       }
6027       if ( nbEdges > 0 )
6028         break;
6029     }
6030     default:
6031     {
6032       for ( int di = -1; di <= 1; di += 2 )
6033       {
6034         size_t j = i + di;
6035         if ( j < pathNodes.size() )
6036         {
6037           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6038           double size2 = dir.SquareMagnitude();
6039           if ( size2 > tol2 )
6040             tangent += dir.Divided( Sqrt( size2 )) * di;
6041         }
6042       }
6043     }
6044     } // switch ( shapeType )
6045
6046     if ( tangent.SquareMagnitude() < tol2 )
6047       return EXTR_CANT_GET_TANGENT;
6048
6049     point.myTgt = tangent;
6050
6051   } // loop on pathNodes
6052
6053
6054   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6055   TTElemOfElemListMap newElemsMap;
6056
6057   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6058
6059   return EXTR_OK;
6060 }
6061
6062 //=======================================================================
6063 //function : linearAngleVariation
6064 //purpose  : spread values over nbSteps
6065 //=======================================================================
6066
6067 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6068                                             list<double>& Angles)
6069 {
6070   int nbAngles = Angles.size();
6071   if( nbSteps > nbAngles && nbAngles > 0 )
6072   {
6073     vector<double> theAngles(nbAngles);
6074     theAngles.assign( Angles.begin(), Angles.end() );
6075
6076     list<double> res;
6077     double rAn2St = double( nbAngles ) / double( nbSteps );
6078     double angPrev = 0, angle;
6079     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6080     {
6081       double angCur = rAn2St * ( iSt+1 );
6082       double angCurFloor  = floor( angCur );
6083       double angPrevFloor = floor( angPrev );
6084       if ( angPrevFloor == angCurFloor )
6085         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6086       else {
6087         int iP = int( angPrevFloor );
6088         double angPrevCeil = ceil(angPrev);
6089         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6090
6091         int iC = int( angCurFloor );
6092         if ( iC < nbAngles )
6093           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6094
6095         iP = int( angPrevCeil );
6096         while ( iC-- > iP )
6097           angle += theAngles[ iC ];
6098       }
6099       res.push_back(angle);
6100       angPrev = angCur;
6101     }
6102     Angles.swap( res );
6103   }
6104 }
6105
6106 //=======================================================================
6107 //function : linearScaleVariation
6108 //purpose  : spread values over nbSteps 
6109 //=======================================================================
6110
6111 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6112                                             std::list<double>& theScales)
6113 {
6114   int nbScales = theScales.size();
6115   std::vector<double> myScales;
6116   myScales.reserve( theNbSteps );
6117   std::list<double>::const_iterator scale = theScales.begin();
6118   double prevScale = 1.0;
6119   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6120   {
6121     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6122     int    stDelta = Max( 1, iStep - myScales.size());
6123     double scDelta = ( *scale - prevScale ) / stDelta;
6124     for ( int iStep = 0; iStep < stDelta; ++iStep )
6125     {
6126       myScales.push_back( prevScale + scDelta );
6127       prevScale = myScales.back();
6128     }
6129     prevScale = *scale;
6130   }
6131   theScales.assign( myScales.begin(), myScales.end() );
6132 }
6133
6134 //================================================================================
6135 /*!
6136  * \brief Move or copy theElements applying theTrsf to their nodes
6137  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6138  *  \param theTrsf - transformation to apply
6139  *  \param theCopy - if true, create translated copies of theElems
6140  *  \param theMakeGroups - if true and theCopy, create translated groups
6141  *  \param theTargetMesh - mesh to copy translated elements into
6142  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6143  */
6144 //================================================================================
6145
6146 SMESH_MeshEditor::PGroupIDs
6147 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6148                              const gp_Trsf&     theTrsf,
6149                              const bool         theCopy,
6150                              const bool         theMakeGroups,
6151                              SMESH_Mesh*        theTargetMesh)
6152 {
6153   ClearLastCreated();
6154   myLastCreatedElems.reserve( theElems.size() );
6155
6156   bool needReverse = false;
6157   string groupPostfix;
6158   switch ( theTrsf.Form() ) {
6159   case gp_PntMirror:
6160     needReverse = true;
6161     groupPostfix = "mirrored";
6162     break;
6163   case gp_Ax1Mirror:
6164     groupPostfix = "mirrored";
6165     break;
6166   case gp_Ax2Mirror:
6167     needReverse = true;
6168     groupPostfix = "mirrored";
6169     break;
6170   case gp_Rotation:
6171     groupPostfix = "rotated";
6172     break;
6173   case gp_Translation:
6174     groupPostfix = "translated";
6175     break;
6176   case gp_Scale:
6177     groupPostfix = "scaled";
6178     break;
6179   case gp_CompoundTrsf: // different scale by axis
6180     groupPostfix = "scaled";
6181     break;
6182   default:
6183     needReverse = false;
6184     groupPostfix = "transformed";
6185   }
6186
6187   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6188   SMESHDS_Mesh* aMesh    = GetMeshDS();
6189
6190   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6191   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6192   SMESH_MeshEditor::ElemFeatures elemType;
6193
6194   // map old node to new one
6195   TNodeNodeMap nodeMap;
6196
6197   // elements sharing moved nodes; those of them which have all
6198   // nodes mirrored but are not in theElems are to be reversed
6199   TIDSortedElemSet inverseElemSet;
6200
6201   // source elements for each generated one
6202   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6203
6204   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6205   TIDSortedElemSet orphanNode;
6206
6207   if ( theElems.empty() ) // transform the whole mesh
6208   {
6209     // add all elements
6210     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6211     while ( eIt->more() ) theElems.insert( eIt->next() );
6212     // add orphan nodes
6213     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6214     while ( nIt->more() )
6215     {
6216       const SMDS_MeshNode* node = nIt->next();
6217       if ( node->NbInverseElements() == 0)
6218         orphanNode.insert( node );
6219     }
6220   }
6221
6222   // loop on elements to transform nodes : first orphan nodes then elems
6223   TIDSortedElemSet::iterator itElem;
6224   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6225   for (int i=0; i<2; i++)
6226     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6227     {
6228       const SMDS_MeshElement* elem = *itElem;
6229       if ( !elem )
6230         continue;
6231
6232       // loop on elem nodes
6233       double coord[3];
6234       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6235       while ( itN->more() )
6236       {
6237         const SMDS_MeshNode* node = cast2Node( itN->next() );
6238         // check if a node has been already transformed
6239         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6240           nodeMap.insert( make_pair ( node, node ));
6241         if ( !n2n_isnew.second )
6242           continue;
6243
6244         node->GetXYZ( coord );
6245         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6246         if ( theTargetMesh ) {
6247           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6248           n2n_isnew.first->second = newNode;
6249           myLastCreatedNodes.push_back(newNode);
6250           srcNodes.push_back( node );
6251         }
6252         else if ( theCopy ) {
6253           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6254           n2n_isnew.first->second = newNode;
6255           myLastCreatedNodes.push_back(newNode);
6256           srcNodes.push_back( node );
6257         }
6258         else {
6259           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6260           // node position on shape becomes invalid
6261           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6262             ( SMDS_SpacePosition::originSpacePosition() );
6263         }
6264
6265         // keep inverse elements
6266         if ( !theCopy && !theTargetMesh && needReverse ) {
6267           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6268           while ( invElemIt->more() ) {
6269             const SMDS_MeshElement* iel = invElemIt->next();
6270             inverseElemSet.insert( iel );
6271           }
6272         }
6273       }
6274     } // loop on elems in { &orphanNode, &theElems };
6275
6276   // either create new elements or reverse mirrored ones
6277   if ( !theCopy && !needReverse && !theTargetMesh )
6278     return PGroupIDs();
6279
6280   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6281
6282   // Replicate or reverse elements
6283
6284   std::vector<int> iForw;
6285   vector<const SMDS_MeshNode*> nodes;
6286   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6287   {
6288     const SMDS_MeshElement* elem = *itElem;
6289     if ( !elem ) continue;
6290
6291     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6292     size_t               nbNodes  = elem->NbNodes();
6293     if ( geomType == SMDSGeom_NONE ) continue; // node
6294
6295     nodes.resize( nbNodes );
6296
6297     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6298     {
6299       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6300       if ( !aPolyedre )
6301         continue;
6302       nodes.clear();
6303       bool allTransformed = true;
6304       int nbFaces = aPolyedre->NbFaces();
6305       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6306       {
6307         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6308         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6309         {
6310           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6311           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6312           if ( nodeMapIt == nodeMap.end() )
6313             allTransformed = false; // not all nodes transformed
6314           else
6315             nodes.push_back((*nodeMapIt).second);
6316         }
6317         if ( needReverse && allTransformed )
6318           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6319       }
6320       if ( !allTransformed )
6321         continue; // not all nodes transformed
6322     }
6323     else // ----------------------- the rest element types
6324     {
6325       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6326       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6327       const vector<int>&    i = needReverse ? iRev : iForw;
6328
6329       // find transformed nodes
6330       size_t iNode = 0;
6331       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6332       while ( itN->more() ) {
6333         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6334         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6335         if ( nodeMapIt == nodeMap.end() )
6336           break; // not all nodes transformed
6337         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6338       }
6339       if ( iNode != nbNodes )
6340         continue; // not all nodes transformed
6341     }
6342
6343     if ( editor ) {
6344       // copy in this or a new mesh
6345       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6346         srcElems.push_back( elem );
6347     }
6348     else {
6349       // reverse element as it was reversed by transformation
6350       if ( nbNodes > 2 )
6351         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6352     }
6353
6354   } // loop on elements
6355
6356   if ( editor && editor != this )
6357     myLastCreatedElems.swap( editor->myLastCreatedElems );
6358
6359   PGroupIDs newGroupIDs;
6360
6361   if ( ( theMakeGroups && theCopy ) ||
6362        ( theMakeGroups && theTargetMesh ) )
6363     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6364
6365   return newGroupIDs;
6366 }
6367
6368 //================================================================================
6369 /*!
6370  * \brief Make an offset mesh from a source 2D mesh
6371  *  \param [in] theElements - source faces
6372  *  \param [in] theValue - offset value
6373  *  \param [out] theTgtMesh - a mesh to add offset elements to
6374  *  \param [in] theMakeGroups - to generate groups
6375  *  \return PGroupIDs - IDs of created groups. NULL means failure
6376  */
6377 //================================================================================
6378
6379 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6380                                                       const double       theValue,
6381                                                       SMESH_Mesh*        theTgtMesh,
6382                                                       const bool         theMakeGroups,
6383                                                       const bool         theCopyElements,
6384                                                       const bool         theFixSelfIntersection)
6385 {
6386   SMESHDS_Mesh*    meshDS = GetMeshDS();
6387   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6388   SMESH_MeshEditor tgtEditor( theTgtMesh );
6389
6390   SMDS_ElemIteratorPtr eIt;
6391   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6392   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6393
6394   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6395   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6396   std::unique_ptr< SMDS_Mesh > offsetMesh
6397     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6398                                    theFixSelfIntersection,
6399                                    new2OldFaces, new2OldNodes ));
6400   if ( offsetMesh->NbElements() == 0 )
6401     return PGroupIDs(); // MakeOffset() failed
6402
6403
6404   if ( theTgtMesh == myMesh && !theCopyElements )
6405   {
6406     // clear the source elements
6407     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6408     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6409     while ( eIt->more() )
6410       meshDS->RemoveFreeElement( eIt->next(), 0 );
6411   }
6412
6413   // offsetMesh->Modified();
6414   // offsetMesh->CompactMesh(); // make IDs start from 1
6415
6416   // source elements for each generated one
6417   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6418   srcElems.reserve( new2OldFaces.size() );
6419   srcNodes.reserve( new2OldNodes.size() );
6420
6421   ClearLastCreated();
6422   myLastCreatedElems.reserve( new2OldFaces.size() );
6423   myLastCreatedNodes.reserve( new2OldNodes.size() );
6424
6425   // copy offsetMesh to theTgtMesh
6426
6427   int idShift = meshDS->MaxNodeID();
6428   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6429     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6430     {
6431 #ifndef _DEBUG_
6432       if ( n->NbInverseElements() > 0 )
6433 #endif
6434       {
6435         const SMDS_MeshNode* n2 =
6436           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6437         myLastCreatedNodes.push_back( n2 );
6438         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6439       }
6440     }
6441
6442   ElemFeatures elemType;
6443   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6444     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6445     {
6446       elemType.Init( f );
6447       elemType.myNodes.clear();
6448       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6449       {
6450         const SMDS_MeshNode* n2 = nIt->next();
6451         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6452       }
6453       tgtEditor.AddElement( elemType.myNodes, elemType );
6454       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6455     }
6456
6457   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6458
6459   PGroupIDs newGroupIDs;
6460   if ( theMakeGroups )
6461     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6462   else
6463     newGroupIDs.reset( new std::list< int > );
6464
6465   return newGroupIDs;
6466 }
6467
6468 //=======================================================================
6469 /*!
6470  * \brief Create groups of elements made during transformation
6471  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6472  *  \param elemGens - elements making corresponding myLastCreatedElems
6473  *  \param postfix - to push_back to names of new groups
6474  *  \param targetMesh - mesh to create groups in
6475  *  \param topPresent - is there are "top" elements that are created by sweeping
6476  */
6477 //=======================================================================
6478
6479 SMESH_MeshEditor::PGroupIDs
6480 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6481                                  const SMESH_SequenceOfElemPtr& elemGens,
6482                                  const std::string&             postfix,
6483                                  SMESH_Mesh*                    targetMesh,
6484                                  const bool                     topPresent)
6485 {
6486   PGroupIDs newGroupIDs( new list<int> );
6487   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6488
6489   // Sort existing groups by types and collect their names
6490
6491   // containers to store an old group and generated new ones;
6492   // 1st new group is for result elems of different type than a source one;
6493   // 2nd new group is for same type result elems ("top" group at extrusion)
6494   using boost::tuple;
6495   using boost::make_tuple;
6496   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6497   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6498   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6499   // group names
6500   set< string > groupNames;
6501
6502   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6503   if ( !groupIt->more() ) return newGroupIDs;
6504
6505   int newGroupID = mesh->GetGroupIds().back()+1;
6506   while ( groupIt->more() )
6507   {
6508     SMESH_Group * group = groupIt->next();
6509     if ( !group ) continue;
6510     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6511     if ( !groupDS || groupDS->IsEmpty() ) continue;
6512     groupNames.insert    ( group->GetName() );
6513     groupDS->SetStoreName( group->GetName() );
6514     const SMDSAbs_ElementType type = groupDS->GetType();
6515     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6516     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6517     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6518     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6519   }
6520
6521   // Loop on nodes and elements to add them in new groups
6522
6523   vector< const SMDS_MeshElement* > resultElems;
6524   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6525   {
6526     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6527     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6528     if ( gens.size() != elems.size() )
6529       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6530
6531     // loop on created elements
6532     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6533     {
6534       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6535       if ( !sourceElem ) {
6536         MESSAGE("generateGroups(): NULL source element");
6537         continue;
6538       }
6539       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6540       if ( groupsOldNew.empty() ) { // no groups of this type at all
6541         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6542           ++iElem; // skip all elements made by sourceElem
6543         continue;
6544       }
6545       // collect all elements made by the iElem-th sourceElem
6546       resultElems.clear();
6547       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6548         if ( resElem != sourceElem )
6549           resultElems.push_back( resElem );
6550       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6551         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6552           if ( resElem != sourceElem )
6553             resultElems.push_back( resElem );
6554
6555       const SMDS_MeshElement* topElem = 0;
6556       if ( isNodes ) // there must be a top element
6557       {
6558         topElem = resultElems.back();
6559         resultElems.pop_back();
6560       }
6561       else
6562       {
6563         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6564         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6565           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6566           {
6567             topElem = *resElemIt;
6568             *resElemIt = 0; // erase *resElemIt
6569             break;
6570           }
6571       }
6572       // add resultElems to groups originted from ones the sourceElem belongs to
6573       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6574       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6575       {
6576         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6577         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6578         {
6579           // fill in a new group
6580           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6581           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6582           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6583             if ( *resElemIt )
6584               newGroup.Add( *resElemIt );
6585
6586           // fill a "top" group
6587           if ( topElem )
6588           {
6589             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6590             newTopGroup.Add( topElem );
6591           }
6592         }
6593       }
6594     } // loop on created elements
6595   }// loop on nodes and elements
6596
6597   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6598
6599   list<int> topGrouIds;
6600   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6601   {
6602     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6603     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6604                                       orderedOldNewGroups[i]->get<2>() };
6605     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6606     {
6607       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6608       if ( newGroupDS->IsEmpty() )
6609       {
6610         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6611       }
6612       else
6613       {
6614         // set group type
6615         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6616
6617         // make a name
6618         const bool isTop = ( topPresent &&
6619                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6620                              is2nd );
6621
6622         string name = oldGroupDS->GetStoreName();
6623         { // remove trailing whitespaces (issue 22599)
6624           size_t size = name.size();
6625           while ( size > 1 && isspace( name[ size-1 ]))
6626             --size;
6627           if ( size != name.size() )
6628           {
6629             name.resize( size );
6630             oldGroupDS->SetStoreName( name.c_str() );
6631           }
6632         }
6633         if ( !targetMesh ) {
6634           string suffix = ( isTop ? "top": postfix.c_str() );
6635           name += "_";
6636           name += suffix;
6637           int nb = 1;
6638           while ( !groupNames.insert( name ).second ) // name exists
6639             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6640         }
6641         else if ( isTop ) {
6642           name += "_top";
6643         }
6644         newGroupDS->SetStoreName( name.c_str() );
6645
6646         // make a SMESH_Groups
6647         mesh->AddGroup( newGroupDS );
6648         if ( isTop )
6649           topGrouIds.push_back( newGroupDS->GetID() );
6650         else
6651           newGroupIDs->push_back( newGroupDS->GetID() );
6652       }
6653     }
6654   }
6655   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6656
6657   return newGroupIDs;
6658 }
6659
6660 //================================================================================
6661 /*!
6662  *  * \brief Return list of group of nodes close to each other within theTolerance
6663  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6664  *  *        an Octree algorithm
6665  *  \param [in,out] theNodes - the nodes to treat
6666  *  \param [in]     theTolerance - the tolerance
6667  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6668  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6669  *         corner and medium nodes in separate groups
6670  */
6671 //================================================================================
6672
6673 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6674                                             const double         theTolerance,
6675                                             TListOfListOfNodes & theGroupsOfNodes,
6676                                             bool                 theSeparateCornersAndMedium)
6677 {
6678   ClearLastCreated();
6679
6680   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6681        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6682        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6683     theSeparateCornersAndMedium = false;
6684
6685   TIDSortedNodeSet& corners = theNodes;
6686   TIDSortedNodeSet  medium;
6687
6688   if ( theNodes.empty() ) // get all nodes in the mesh
6689   {
6690     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6691     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6692     if ( theSeparateCornersAndMedium )
6693       while ( nIt->more() )
6694       {
6695         const SMDS_MeshNode* n = nIt->next();
6696         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6697         nodeSet->insert( nodeSet->end(), n );
6698       }
6699     else
6700       while ( nIt->more() )
6701         theNodes.insert( theNodes.end(), nIt->next() );
6702   }
6703   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6704   {
6705     TIDSortedNodeSet::iterator nIt = corners.begin();
6706     while ( nIt != corners.end() )
6707       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6708       {
6709         medium.insert( medium.end(), *nIt );
6710         corners.erase( nIt++ );
6711       }
6712       else
6713       {
6714         ++nIt;
6715       }
6716   }
6717
6718   if ( !corners.empty() )
6719     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6720   if ( !medium.empty() )
6721     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6722 }
6723
6724 //=======================================================================
6725 //function : SimplifyFace
6726 //purpose  : split a chain of nodes into several closed chains
6727 //=======================================================================
6728
6729 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6730                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6731                                     vector<int>&                         quantities) const
6732 {
6733   int nbNodes = faceNodes.size();
6734   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6735     --nbNodes;
6736   if ( nbNodes < 3 )
6737     return 0;
6738   size_t prevNbQuant = quantities.size();
6739
6740   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6741   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6742   map< const SMDS_MeshNode*, int >::iterator nInd;
6743
6744   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6745   simpleNodes.push_back( faceNodes[0] );
6746   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6747   {
6748     if ( faceNodes[ iCur ] != simpleNodes.back() )
6749     {
6750       int index = simpleNodes.size();
6751       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6752       int prevIndex = nInd->second;
6753       if ( prevIndex < index )
6754       {
6755         // a sub-loop found
6756         int loopLen = index - prevIndex;
6757         if ( loopLen > 2 )
6758         {
6759           // store the sub-loop
6760           quantities.push_back( loopLen );
6761           for ( int i = prevIndex; i < index; i++ )
6762             poly_nodes.push_back( simpleNodes[ i ]);
6763         }
6764         simpleNodes.resize( prevIndex+1 );
6765       }
6766       else
6767       {
6768         simpleNodes.push_back( faceNodes[ iCur ]);
6769       }
6770     }
6771   }
6772
6773   if ( simpleNodes.size() > 2 )
6774   {
6775     quantities.push_back( simpleNodes.size() );
6776     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6777   }
6778
6779   return quantities.size() - prevNbQuant;
6780 }
6781
6782 //=======================================================================
6783 //function : MergeNodes
6784 //purpose  : In each group, the cdr of nodes are substituted by the first one
6785 //           in all elements.
6786 //=======================================================================
6787
6788 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6789                                    const bool           theAvoidMakingHoles)
6790 {
6791   ClearLastCreated();
6792
6793   SMESHDS_Mesh* mesh = GetMeshDS();
6794
6795   TNodeNodeMap nodeNodeMap; // node to replace - new node
6796   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6797   list< int > rmElemIds, rmNodeIds;
6798   vector< ElemFeatures > newElemDefs;
6799
6800   // Fill nodeNodeMap and elems
6801
6802   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6803   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6804   {
6805     list<const SMDS_MeshNode*>& nodes = *grIt;
6806     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6807     const SMDS_MeshNode* nToKeep = *nIt;
6808     for ( ++nIt; nIt != nodes.end(); nIt++ )
6809     {
6810       const SMDS_MeshNode* nToRemove = *nIt;
6811       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6812       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6813       while ( invElemIt->more() ) {
6814         const SMDS_MeshElement* elem = invElemIt->next();
6815         elems.insert(elem);
6816       }
6817     }
6818   }
6819
6820   // Apply recursive replacements (BUG 0020185)
6821   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6822   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6823   {
6824     const SMDS_MeshNode* nToKeep = nnIt->second;
6825     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6826     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6827     {
6828       nToKeep = nnIt_i->second;
6829       nnIt->second = nToKeep;
6830       nnIt_i = nodeNodeMap.find( nToKeep );
6831     }
6832   }
6833
6834   if ( theAvoidMakingHoles )
6835   {
6836     // find elements whose topology changes
6837
6838     vector<const SMDS_MeshElement*> pbElems;
6839     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6840     for ( ; eIt != elems.end(); ++eIt )
6841     {
6842       const SMDS_MeshElement* elem = *eIt;
6843       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6844       while ( itN->more() )
6845       {
6846         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6847         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6848         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6849         {
6850           // several nodes of elem stick
6851           pbElems.push_back( elem );
6852           break;
6853         }
6854       }
6855     }
6856     // exclude from merge nodes causing spoiling element
6857     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6858     {
6859       bool nodesExcluded = false;
6860       for ( size_t i = 0; i < pbElems.size(); ++i )
6861       {
6862         size_t prevNbMergeNodes = nodeNodeMap.size();
6863         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6864              prevNbMergeNodes < nodeNodeMap.size() )
6865           nodesExcluded = true;
6866       }
6867       if ( !nodesExcluded )
6868         break;
6869     }
6870   }
6871
6872   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6873   {
6874     const SMDS_MeshNode* nToRemove = nnIt->first;
6875     const SMDS_MeshNode* nToKeep   = nnIt->second;
6876     if ( nToRemove != nToKeep )
6877     {
6878       rmNodeIds.push_back( nToRemove->GetID() );
6879       AddToSameGroups( nToKeep, nToRemove, mesh );
6880       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6881       // w/o creating node in place of merged ones.
6882       SMDS_PositionPtr pos = nToRemove->GetPosition();
6883       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6884         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6885           sm->SetIsAlwaysComputed( true );
6886     }
6887   }
6888
6889   // Change element nodes or remove an element
6890
6891   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6892   for ( ; eIt != elems.end(); eIt++ )
6893   {
6894     const SMDS_MeshElement* elem = *eIt;
6895     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6896
6897     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6898     if ( !keepElem )
6899       rmElemIds.push_back( elem->GetID() );
6900
6901     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6902     {
6903       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6904                                                & newElemDefs[i].myNodes[0],
6905                                                newElemDefs[i].myNodes.size() ))
6906       {
6907         if ( i == 0 )
6908         {
6909           newElemDefs[i].SetID( elem->GetID() );
6910           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6911           if ( !keepElem ) rmElemIds.pop_back();
6912         }
6913         else
6914         {
6915           newElemDefs[i].SetID( -1 );
6916         }
6917         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6918         if ( sm && newElem )
6919           sm->AddElement( newElem );
6920         if ( elem != newElem )
6921           ReplaceElemInGroups( elem, newElem, mesh );
6922       }
6923     }
6924   }
6925
6926   // Remove bad elements, then equal nodes (order important)
6927   Remove( rmElemIds, /*isNodes=*/false );
6928   Remove( rmNodeIds, /*isNodes=*/true );
6929
6930   return;
6931 }
6932
6933 //=======================================================================
6934 //function : applyMerge
6935 //purpose  : Compute new connectivity of an element after merging nodes
6936 //  \param [in] elems - the element
6937 //  \param [out] newElemDefs - definition(s) of result element(s)
6938 //  \param [inout] nodeNodeMap - nodes to merge
6939 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6940 //              after merging (but not degenerated), removes nodes causing
6941 //              the invalidity from \a nodeNodeMap.
6942 //  \return bool - true if the element should be removed
6943 //=======================================================================
6944
6945 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6946                                    vector< ElemFeatures >& newElemDefs,
6947                                    TNodeNodeMap&           nodeNodeMap,
6948                                    const bool              avoidMakingHoles )
6949 {
6950   bool toRemove = false; // to remove elem
6951   int nbResElems = 1;    // nb new elements
6952
6953   newElemDefs.resize(nbResElems);
6954   newElemDefs[0].Init( elem );
6955   newElemDefs[0].myNodes.clear();
6956
6957   set<const SMDS_MeshNode*> nodeSet;
6958   vector< const SMDS_MeshNode*>   curNodes;
6959   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6960   vector<int> iRepl;
6961
6962   const        int  nbNodes = elem->NbNodes();
6963   SMDSAbs_EntityType entity = elem->GetEntityType();
6964
6965   curNodes.resize( nbNodes );
6966   uniqueNodes.resize( nbNodes );
6967   iRepl.resize( nbNodes );
6968   int iUnique = 0, iCur = 0, nbRepl = 0;
6969
6970   // Get new seq of nodes
6971
6972   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6973   while ( itN->more() )
6974   {
6975     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6976
6977     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6978     if ( nnIt != nodeNodeMap.end() ) {
6979       n = (*nnIt).second;
6980     }
6981     curNodes[ iCur ] = n;
6982     bool isUnique = nodeSet.insert( n ).second;
6983     if ( isUnique )
6984       uniqueNodes[ iUnique++ ] = n;
6985     else
6986       iRepl[ nbRepl++ ] = iCur;
6987     iCur++;
6988   }
6989
6990   // Analyse element topology after replacement
6991
6992   int nbUniqueNodes = nodeSet.size();
6993   if ( nbNodes != nbUniqueNodes ) // some nodes stick
6994   {
6995     toRemove = true;
6996     nbResElems = 0;
6997
6998     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
6999     {
7000       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7001       int nbCorners = nbNodes / 2;
7002       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7003       {
7004         int iNext = ( iCur + 1 ) % nbCorners;
7005         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7006         {
7007           int iMedium = iCur + nbCorners;
7008           vector< const SMDS_MeshNode* >::iterator i =
7009             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7010                        uniqueNodes.end(),
7011                        curNodes[ iMedium ]);
7012           if ( i != uniqueNodes.end() )
7013           {
7014             --nbUniqueNodes;
7015             for ( ; i+1 != uniqueNodes.end(); ++i )
7016               *i = *(i+1);
7017           }
7018         }
7019       }
7020     }
7021
7022     switch ( entity )
7023     {
7024     case SMDSEntity_Polygon:
7025     case SMDSEntity_Quad_Polygon: // Polygon
7026     {
7027       ElemFeatures* elemType = & newElemDefs[0];
7028       const bool isQuad = elemType->myIsQuad;
7029       if ( isQuad )
7030         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7031           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7032
7033       // a polygon can divide into several elements
7034       vector<const SMDS_MeshNode *> polygons_nodes;
7035       vector<int> quantities;
7036       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7037       newElemDefs.resize( nbResElems );
7038       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7039       {
7040         ElemFeatures* elemType = & newElemDefs[iface];
7041         if ( iface ) elemType->Init( elem );
7042
7043         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7044         int nbNewNodes = quantities[iface];
7045         face_nodes.assign( polygons_nodes.begin() + inode,
7046                            polygons_nodes.begin() + inode + nbNewNodes );
7047         inode += nbNewNodes;
7048         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7049         {
7050           bool isValid = ( nbNewNodes % 2 == 0 );
7051           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7052             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7053           elemType->SetQuad( isValid );
7054           if ( isValid ) // put medium nodes after corners
7055             SMDS_MeshCell::applyInterlaceRev
7056               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7057                                                     nbNewNodes ), face_nodes );
7058         }
7059         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7060       }
7061       nbUniqueNodes = newElemDefs[0].myNodes.size();
7062       break;
7063     } // Polygon
7064
7065     case SMDSEntity_Polyhedra: // Polyhedral volume
7066     {
7067       if ( nbUniqueNodes >= 4 )
7068       {
7069         // each face has to be analyzed in order to check volume validity
7070         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7071         {
7072           int nbFaces = aPolyedre->NbFaces();
7073
7074           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7075           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7076           vector<const SMDS_MeshNode *>  faceNodes;
7077           poly_nodes.clear();
7078           quantities.clear();
7079
7080           for (int iface = 1; iface <= nbFaces; iface++)
7081           {
7082             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7083             faceNodes.resize( nbFaceNodes );
7084             for (int inode = 1; inode <= nbFaceNodes; inode++)
7085             {
7086               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7087               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7088               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7089                 faceNode = (*nnIt).second;
7090               faceNodes[inode - 1] = faceNode;
7091             }
7092             SimplifyFace(faceNodes, poly_nodes, quantities);
7093           }
7094
7095           if ( quantities.size() > 3 )
7096           {
7097             // TODO: remove coincident faces
7098             nbResElems = 1;
7099             nbUniqueNodes = newElemDefs[0].myNodes.size();
7100           }
7101         }
7102       }
7103     }
7104     break;
7105
7106     // Regular elements
7107     // TODO not all the possible cases are solved. Find something more generic?
7108     case SMDSEntity_Edge: //////// EDGE
7109     case SMDSEntity_Triangle: //// TRIANGLE
7110     case SMDSEntity_Quad_Triangle:
7111     case SMDSEntity_Tetra:
7112     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7113     {
7114       break;
7115     }
7116     case SMDSEntity_Quad_Edge:
7117     {
7118       break;
7119     }
7120     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7121     {
7122       if ( nbUniqueNodes < 3 )
7123         toRemove = true;
7124       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7125         toRemove = true; // opposite nodes stick
7126       else
7127         toRemove = false;
7128       break;
7129     }
7130     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7131     {
7132       //   1    5    2
7133       //    +---+---+
7134       //    |       |
7135       //   4+       +6
7136       //    |       |
7137       //    +---+---+
7138       //   0    7    3
7139       if ( nbUniqueNodes == 6 &&
7140            iRepl[0] < 4       &&
7141            ( nbRepl == 1 || iRepl[1] >= 4 ))
7142       {
7143         toRemove = false;
7144       }
7145       break;
7146     }
7147     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7148     {
7149       //   1    5    2
7150       //    +---+---+
7151       //    |       |
7152       //   4+  8+   +6
7153       //    |       |
7154       //    +---+---+
7155       //   0    7    3
7156       if ( nbUniqueNodes == 7 &&
7157            iRepl[0] < 4       &&
7158            ( nbRepl == 1 || iRepl[1] != 8 ))
7159       {
7160         toRemove = false;
7161       }
7162       break;
7163     }
7164     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7165     {
7166       if ( nbUniqueNodes == 4 ) {
7167         // ---------------------------------> tetrahedron
7168         if ( curNodes[3] == curNodes[4] &&
7169              curNodes[3] == curNodes[5] ) {
7170           // top nodes stick
7171           toRemove = false;
7172         }
7173         else if ( curNodes[0] == curNodes[1] &&
7174                   curNodes[0] == curNodes[2] ) {
7175           // bottom nodes stick: set a top before
7176           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7177           uniqueNodes[ 0 ] = curNodes [ 5 ];
7178           uniqueNodes[ 1 ] = curNodes [ 4 ];
7179           uniqueNodes[ 2 ] = curNodes [ 3 ];
7180           toRemove = false;
7181         }
7182         else if (( curNodes[0] == curNodes[3] ) +
7183                  ( curNodes[1] == curNodes[4] ) +
7184                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7185           // a lateral face turns into a line
7186           toRemove = false;
7187         }
7188       }
7189       else if ( nbUniqueNodes == 5 ) {
7190         // PENTAHEDRON --------------------> pyramid
7191         if ( curNodes[0] == curNodes[3] )
7192         {
7193           uniqueNodes[ 0 ] = curNodes[ 1 ];
7194           uniqueNodes[ 1 ] = curNodes[ 4 ];
7195           uniqueNodes[ 2 ] = curNodes[ 5 ];
7196           uniqueNodes[ 3 ] = curNodes[ 2 ];
7197           uniqueNodes[ 4 ] = curNodes[ 0 ];
7198           toRemove = false;
7199         }
7200         if ( curNodes[1] == curNodes[4] )
7201         {
7202           uniqueNodes[ 0 ] = curNodes[ 0 ];
7203           uniqueNodes[ 1 ] = curNodes[ 2 ];
7204           uniqueNodes[ 2 ] = curNodes[ 5 ];
7205           uniqueNodes[ 3 ] = curNodes[ 3 ];
7206           uniqueNodes[ 4 ] = curNodes[ 1 ];
7207           toRemove = false;
7208         }
7209         if ( curNodes[2] == curNodes[5] )
7210         {
7211           uniqueNodes[ 0 ] = curNodes[ 0 ];
7212           uniqueNodes[ 1 ] = curNodes[ 3 ];
7213           uniqueNodes[ 2 ] = curNodes[ 4 ];
7214           uniqueNodes[ 3 ] = curNodes[ 1 ];
7215           uniqueNodes[ 4 ] = curNodes[ 2 ];
7216           toRemove = false;
7217         }
7218       }
7219       break;
7220     }
7221     case SMDSEntity_Hexa:
7222     {
7223       //////////////////////////////////// HEXAHEDRON
7224       SMDS_VolumeTool hexa (elem);
7225       hexa.SetExternalNormal();
7226       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7227         //////////////////////// HEX ---> tetrahedron
7228         for ( int iFace = 0; iFace < 6; iFace++ ) {
7229           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7230           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7231               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7232               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7233             // one face turns into a point ...
7234             int  pickInd = ind[ 0 ];
7235             int iOppFace = hexa.GetOppFaceIndex( iFace );
7236             ind = hexa.GetFaceNodesIndices( iOppFace );
7237             int nbStick = 0;
7238             uniqueNodes.clear();
7239             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7240               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7241                 nbStick++;
7242               else
7243                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7244             }
7245             if ( nbStick == 1 ) {
7246               // ... and the opposite one - into a triangle.
7247               // set a top node
7248               uniqueNodes.push_back( curNodes[ pickInd ]);
7249               toRemove = false;
7250             }
7251             break;
7252           }
7253         }
7254       }
7255       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7256         //////////////////////// HEX ---> prism
7257         int nbTria = 0, iTria[3];
7258         const int *ind; // indices of face nodes
7259         // look for triangular faces
7260         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7261           ind = hexa.GetFaceNodesIndices( iFace );
7262           TIDSortedNodeSet faceNodes;
7263           for ( iCur = 0; iCur < 4; iCur++ )
7264             faceNodes.insert( curNodes[ind[iCur]] );
7265           if ( faceNodes.size() == 3 )
7266             iTria[ nbTria++ ] = iFace;
7267         }
7268         // check if triangles are opposite
7269         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7270         {
7271           // set nodes of the bottom triangle
7272           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7273           vector<int> indB;
7274           for ( iCur = 0; iCur < 4; iCur++ )
7275             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7276               indB.push_back( ind[iCur] );
7277           if ( !hexa.IsForward() )
7278             std::swap( indB[0], indB[2] );
7279           for ( iCur = 0; iCur < 3; iCur++ )
7280             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7281           // set nodes of the top triangle
7282           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7283           for ( iCur = 0; iCur < 3; ++iCur )
7284             for ( int j = 0; j < 4; ++j )
7285               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7286               {
7287                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7288                 break;
7289               }
7290           toRemove = false;
7291           break;
7292         }
7293       }
7294       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7295         //////////////////// HEXAHEDRON ---> pyramid
7296         for ( int iFace = 0; iFace < 6; iFace++ ) {
7297           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7298           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7299               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7300               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7301             // one face turns into a point ...
7302             int iOppFace = hexa.GetOppFaceIndex( iFace );
7303             ind = hexa.GetFaceNodesIndices( iOppFace );
7304             uniqueNodes.clear();
7305             for ( iCur = 0; iCur < 4; iCur++ ) {
7306               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7307                 break;
7308               else
7309                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7310             }
7311             if ( uniqueNodes.size() == 4 ) {
7312               // ... and the opposite one is a quadrangle
7313               // set a top node
7314               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7315               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7316               toRemove = false;
7317             }
7318             break;
7319           }
7320         }
7321       }
7322
7323       if ( toRemove && nbUniqueNodes > 4 ) {
7324         ////////////////// HEXAHEDRON ---> polyhedron
7325         hexa.SetExternalNormal();
7326         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7327         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7328         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7329         quantities.reserve( 6 );     quantities.clear();
7330         for ( int iFace = 0; iFace < 6; iFace++ )
7331         {
7332           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7333           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7334                curNodes[ind[1]] == curNodes[ind[3]] )
7335           {
7336             quantities.clear();
7337             break; // opposite nodes stick
7338           }
7339           nodeSet.clear();
7340           for ( iCur = 0; iCur < 4; iCur++ )
7341           {
7342             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7343               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7344           }
7345           if ( nodeSet.size() < 3 )
7346             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7347           else
7348             quantities.push_back( nodeSet.size() );
7349         }
7350         if ( quantities.size() >= 4 )
7351         {
7352           nbResElems = 1;
7353           nbUniqueNodes = poly_nodes.size();
7354           newElemDefs[0].SetPoly(true);
7355         }
7356       }
7357       break;
7358     } // case HEXAHEDRON
7359
7360     default:
7361       toRemove = true;
7362
7363     } // switch ( entity )
7364
7365     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7366     {
7367       // erase from nodeNodeMap nodes whose merge spoils elem
7368       vector< const SMDS_MeshNode* > noMergeNodes;
7369       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7370       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7371         nodeNodeMap.erase( noMergeNodes[i] );
7372     }
7373     
7374   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7375
7376   uniqueNodes.resize( nbUniqueNodes );
7377
7378   if ( !toRemove && nbResElems == 0 )
7379     nbResElems = 1;
7380
7381   newElemDefs.resize( nbResElems );
7382
7383   return !toRemove;
7384 }
7385
7386
7387 // ========================================================
7388 // class   : ComparableElement
7389 // purpose : allow comparing elements basing on their nodes
7390 // ========================================================
7391
7392 class ComparableElement : public boost::container::flat_set< int >
7393 {
7394   typedef boost::container::flat_set< int >  int_set;
7395
7396   const SMDS_MeshElement* myElem;
7397   int                     mySumID;
7398   mutable int             myGroupID;
7399
7400 public:
7401
7402   ComparableElement( const SMDS_MeshElement* theElem ):
7403     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7404   {
7405     this->reserve( theElem->NbNodes() );
7406     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7407     {
7408       int id = nodeIt->next()->GetID();
7409       mySumID += id;
7410       this->insert( id );
7411     }
7412   }
7413
7414   const SMDS_MeshElement* GetElem() const { return myElem; }
7415
7416   int& GroupID() const { return myGroupID; }
7417   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7418
7419   ComparableElement( const ComparableElement& theSource ) // move copy
7420   {
7421     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7422     (int_set&) (*this ) = boost::move( src );
7423     myElem    = src.myElem;
7424     mySumID   = src.mySumID;
7425     myGroupID = src.myGroupID;
7426   }
7427
7428   static int HashCode(const ComparableElement& se, int limit )
7429   {
7430     return ::HashCode( se.mySumID, limit );
7431   }
7432   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7433   {
7434     return ( se1 == se2 );
7435   }
7436
7437 };
7438
7439 //=======================================================================
7440 //function : FindEqualElements
7441 //purpose  : Return list of group of elements built on the same nodes.
7442 //           Search among theElements or in the whole mesh if theElements is empty
7443 //=======================================================================
7444
7445 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7446                                           TListOfListOfElementsID & theGroupsOfElementsID )
7447 {
7448   ClearLastCreated();
7449
7450   SMDS_ElemIteratorPtr elemIt;
7451   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7452   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7453
7454   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7455   typedef std::list<int>                                          TGroupOfElems;
7456   TMapOfElements               mapOfElements;
7457   std::vector< TGroupOfElems > arrayOfGroups;
7458   TGroupOfElems                groupOfElems;
7459
7460   while ( elemIt->more() )
7461   {
7462     const SMDS_MeshElement* curElem = elemIt->next();
7463     ComparableElement      compElem = curElem;
7464     // check uniqueness
7465     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7466     if ( elemInSet.GetElem() != curElem ) // coincident elem
7467     {
7468       int& iG = elemInSet.GroupID();
7469       if ( iG < 0 )
7470       {
7471         iG = arrayOfGroups.size();
7472         arrayOfGroups.push_back( groupOfElems );
7473         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7474       }
7475       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7476     }
7477   }
7478
7479   groupOfElems.clear();
7480   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7481   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7482   {
7483     if ( groupIt->size() > 1 ) {
7484       //groupOfElems.sort(); -- theElements are sorted already
7485       theGroupsOfElementsID.emplace_back( *groupIt );
7486     }
7487   }
7488 }
7489
7490 //=======================================================================
7491 //function : MergeElements
7492 //purpose  : In each given group, substitute all elements by the first one.
7493 //=======================================================================
7494
7495 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7496 {
7497   ClearLastCreated();
7498
7499   typedef list<int> TListOfIDs;
7500   TListOfIDs rmElemIds; // IDs of elems to remove
7501
7502   SMESHDS_Mesh* aMesh = GetMeshDS();
7503
7504   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7505   while ( groupsIt != theGroupsOfElementsID.end() ) {
7506     TListOfIDs& aGroupOfElemID = *groupsIt;
7507     aGroupOfElemID.sort();
7508     int elemIDToKeep = aGroupOfElemID.front();
7509     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7510     aGroupOfElemID.pop_front();
7511     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7512     while ( idIt != aGroupOfElemID.end() ) {
7513       int elemIDToRemove = *idIt;
7514       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7515       // add the kept element in groups of removed one (PAL15188)
7516       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7517       rmElemIds.push_back( elemIDToRemove );
7518       ++idIt;
7519     }
7520     ++groupsIt;
7521   }
7522
7523   Remove( rmElemIds, false );
7524 }
7525
7526 //=======================================================================
7527 //function : MergeEqualElements
7528 //purpose  : Remove all but one of elements built on the same nodes.
7529 //=======================================================================
7530
7531 void SMESH_MeshEditor::MergeEqualElements()
7532 {
7533   TIDSortedElemSet aMeshElements; /* empty input ==
7534                                      to merge equal elements in the whole mesh */
7535   TListOfListOfElementsID aGroupsOfElementsID;
7536   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7537   MergeElements( aGroupsOfElementsID );
7538 }
7539
7540 //=======================================================================
7541 //function : findAdjacentFace
7542 //purpose  :
7543 //=======================================================================
7544
7545 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7546                                                 const SMDS_MeshNode* n2,
7547                                                 const SMDS_MeshElement* elem)
7548 {
7549   TIDSortedElemSet elemSet, avoidSet;
7550   if ( elem )
7551     avoidSet.insert ( elem );
7552   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7553 }
7554
7555 //=======================================================================
7556 //function : findSegment
7557 //purpose  : Return a mesh segment by two nodes one of which can be medium
7558 //=======================================================================
7559
7560 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7561                                            const SMDS_MeshNode* n2)
7562 {
7563   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7564   while ( it->more() )
7565   {
7566     const SMDS_MeshElement* seg = it->next();
7567     if ( seg->GetNodeIndex( n2 ) >= 0 )
7568       return seg;
7569   }
7570   return 0;
7571 }
7572
7573 //=======================================================================
7574 //function : FindFreeBorder
7575 //purpose  :
7576 //=======================================================================
7577
7578 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7579
7580 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7581                                        const SMDS_MeshNode*             theSecondNode,
7582                                        const SMDS_MeshNode*             theLastNode,
7583                                        list< const SMDS_MeshNode* > &   theNodes,
7584                                        list< const SMDS_MeshElement* >& theFaces)
7585 {
7586   if ( !theFirstNode || !theSecondNode )
7587     return false;
7588   // find border face between theFirstNode and theSecondNode
7589   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7590   if ( !curElem )
7591     return false;
7592
7593   theFaces.push_back( curElem );
7594   theNodes.push_back( theFirstNode );
7595   theNodes.push_back( theSecondNode );
7596
7597   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7598   //TIDSortedElemSet foundElems;
7599   bool needTheLast = ( theLastNode != 0 );
7600
7601   vector<const SMDS_MeshNode*> nodes;
7602   
7603   while ( nStart != theLastNode ) {
7604     if ( nStart == theFirstNode )
7605       return !needTheLast;
7606
7607     // find all free border faces sharing nStart
7608
7609     list< const SMDS_MeshElement* > curElemList;
7610     list< const SMDS_MeshNode* >    nStartList;
7611     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7612     while ( invElemIt->more() ) {
7613       const SMDS_MeshElement* e = invElemIt->next();
7614       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7615       {
7616         // get nodes
7617         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7618                       SMDS_MeshElement::iterator() );
7619         nodes.push_back( nodes[ 0 ]);
7620
7621         // check 2 links
7622         int iNode = 0, nbNodes = nodes.size() - 1;
7623         for ( iNode = 0; iNode < nbNodes; iNode++ )
7624           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7625                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7626               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7627           {
7628             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7629             curElemList.push_back( e );
7630           }
7631       }
7632     }
7633     // analyse the found
7634
7635     int nbNewBorders = curElemList.size();
7636     if ( nbNewBorders == 0 ) {
7637       // no free border furthermore
7638       return !needTheLast;
7639     }
7640     else if ( nbNewBorders == 1 ) {
7641       // one more element found
7642       nIgnore = nStart;
7643       nStart = nStartList.front();
7644       curElem = curElemList.front();
7645       theFaces.push_back( curElem );
7646       theNodes.push_back( nStart );
7647     }
7648     else {
7649       // several continuations found
7650       list< const SMDS_MeshElement* >::iterator curElemIt;
7651       list< const SMDS_MeshNode* >::iterator nStartIt;
7652       // check if one of them reached the last node
7653       if ( needTheLast ) {
7654         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7655              curElemIt!= curElemList.end();
7656              curElemIt++, nStartIt++ )
7657           if ( *nStartIt == theLastNode ) {
7658             theFaces.push_back( *curElemIt );
7659             theNodes.push_back( *nStartIt );
7660             return true;
7661           }
7662       }
7663       // find the best free border by the continuations
7664       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7665       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7666       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7667            curElemIt!= curElemList.end();
7668            curElemIt++, nStartIt++ )
7669       {
7670         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7671         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7672         // find one more free border
7673         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7674           cNL->clear();
7675           cFL->clear();
7676         }
7677         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7678           // choice: clear a worse one
7679           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7680           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7681           contNodes[ iWorse ].clear();
7682           contFaces[ iWorse ].clear();
7683         }
7684       }
7685       if ( contNodes[0].empty() && contNodes[1].empty() )
7686         return false;
7687
7688       // push_back the best free border
7689       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7690       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7691       //theNodes.pop_back(); // remove nIgnore
7692       theNodes.pop_back(); // remove nStart
7693       //theFaces.pop_back(); // remove curElem
7694       theNodes.splice( theNodes.end(), *cNL );
7695       theFaces.splice( theFaces.end(), *cFL );
7696       return true;
7697
7698     } // several continuations found
7699   } // while ( nStart != theLastNode )
7700
7701   return true;
7702 }
7703
7704 //=======================================================================
7705 //function : CheckFreeBorderNodes
7706 //purpose  : Return true if the tree nodes are on a free border
7707 //=======================================================================
7708
7709 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7710                                             const SMDS_MeshNode* theNode2,
7711                                             const SMDS_MeshNode* theNode3)
7712 {
7713   list< const SMDS_MeshNode* > nodes;
7714   list< const SMDS_MeshElement* > faces;
7715   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7716 }
7717
7718 //=======================================================================
7719 //function : SewFreeBorder
7720 //purpose  :
7721 //warning  : for border-to-side sewing theSideSecondNode is considered as
7722 //           the last side node and theSideThirdNode is not used
7723 //=======================================================================
7724
7725 SMESH_MeshEditor::Sew_Error
7726 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7727                                  const SMDS_MeshNode* theBordSecondNode,
7728                                  const SMDS_MeshNode* theBordLastNode,
7729                                  const SMDS_MeshNode* theSideFirstNode,
7730                                  const SMDS_MeshNode* theSideSecondNode,
7731                                  const SMDS_MeshNode* theSideThirdNode,
7732                                  const bool           theSideIsFreeBorder,
7733                                  const bool           toCreatePolygons,
7734                                  const bool           toCreatePolyedrs)
7735 {
7736   ClearLastCreated();
7737
7738   Sew_Error aResult = SEW_OK;
7739
7740   // ====================================
7741   //    find side nodes and elements
7742   // ====================================
7743
7744   list< const SMDS_MeshNode* >    nSide[ 2 ];
7745   list< const SMDS_MeshElement* > eSide[ 2 ];
7746   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7747   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7748
7749   // Free border 1
7750   // --------------
7751   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7752                       nSide[0], eSide[0])) {
7753     MESSAGE(" Free Border 1 not found " );
7754     aResult = SEW_BORDER1_NOT_FOUND;
7755   }
7756   if (theSideIsFreeBorder) {
7757     // Free border 2
7758     // --------------
7759     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7760                         nSide[1], eSide[1])) {
7761       MESSAGE(" Free Border 2 not found " );
7762       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7763     }
7764   }
7765   if ( aResult != SEW_OK )
7766     return aResult;
7767
7768   if (!theSideIsFreeBorder) {
7769     // Side 2
7770     // --------------
7771
7772     // -------------------------------------------------------------------------
7773     // Algo:
7774     // 1. If nodes to merge are not coincident, move nodes of the free border
7775     //    from the coord sys defined by the direction from the first to last
7776     //    nodes of the border to the correspondent sys of the side 2
7777     // 2. On the side 2, find the links most co-directed with the correspondent
7778     //    links of the free border
7779     // -------------------------------------------------------------------------
7780
7781     // 1. Since sewing may break if there are volumes to split on the side 2,
7782     //    we won't move nodes but just compute new coordinates for them
7783     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7784     TNodeXYZMap nBordXYZ;
7785     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7786     list< const SMDS_MeshNode* >::iterator nBordIt;
7787
7788     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7789     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7790     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7791     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7792     double tol2 = 1.e-8;
7793     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7794     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7795       // Need node movement.
7796
7797       // find X and Z axes to create trsf
7798       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7799       gp_Vec X = Zs ^ Zb;
7800       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7801         // Zb || Zs
7802         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7803
7804       // coord systems
7805       gp_Ax3 toBordAx( Pb1, Zb, X );
7806       gp_Ax3 fromSideAx( Ps1, Zs, X );
7807       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7808       // set trsf
7809       gp_Trsf toBordSys, fromSide2Sys;
7810       toBordSys.SetTransformation( toBordAx );
7811       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7812       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7813
7814       // move
7815       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7816         const SMDS_MeshNode* n = *nBordIt;
7817         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7818         toBordSys.Transforms( xyz );
7819         fromSide2Sys.Transforms( xyz );
7820         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7821       }
7822     }
7823     else {
7824       // just insert nodes XYZ in the nBordXYZ map
7825       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7826         const SMDS_MeshNode* n = *nBordIt;
7827         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7828       }
7829     }
7830
7831     // 2. On the side 2, find the links most co-directed with the correspondent
7832     //    links of the free border
7833
7834     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7835     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7836     sideNodes.push_back( theSideFirstNode );
7837
7838     bool hasVolumes = false;
7839     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7840     set<long> foundSideLinkIDs, checkedLinkIDs;
7841     SMDS_VolumeTool volume;
7842     //const SMDS_MeshNode* faceNodes[ 4 ];
7843
7844     const SMDS_MeshNode*    sideNode;
7845     const SMDS_MeshElement* sideElem  = 0;
7846     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7847     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7848     nBordIt = bordNodes.begin();
7849     nBordIt++;
7850     // border node position and border link direction to compare with
7851     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7852     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7853     // choose next side node by link direction or by closeness to
7854     // the current border node:
7855     bool searchByDir = ( *nBordIt != theBordLastNode );
7856     do {
7857       // find the next node on the Side 2
7858       sideNode = 0;
7859       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7860       long linkID;
7861       checkedLinkIDs.clear();
7862       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7863
7864       // loop on inverse elements of current node (prevSideNode) on the Side 2
7865       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7866       while ( invElemIt->more() )
7867       {
7868         const SMDS_MeshElement* elem = invElemIt->next();
7869         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7870         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7871         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7872         bool isVolume = volume.Set( elem );
7873         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7874         if ( isVolume ) // --volume
7875           hasVolumes = true;
7876         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7877           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7878           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7879           while ( nIt->more() ) {
7880             nodes[ iNode ] = cast2Node( nIt->next() );
7881             if ( nodes[ iNode++ ] == prevSideNode )
7882               iPrevNode = iNode - 1;
7883           }
7884           // there are 2 links to check
7885           nbNodes = 2;
7886         }
7887         else // --edge
7888           continue;
7889         // loop on links, to be precise, on the second node of links
7890         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7891           const SMDS_MeshNode* n = nodes[ iNode ];
7892           if ( isVolume ) {
7893             if ( !volume.IsLinked( n, prevSideNode ))
7894               continue;
7895           }
7896           else {
7897             if ( iNode ) // a node before prevSideNode
7898               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7899             else         // a node after prevSideNode
7900               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7901           }
7902           // check if this link was already used
7903           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7904           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7905           if (!isJustChecked &&
7906               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7907           {
7908             // test a link geometrically
7909             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7910             bool linkIsBetter = false;
7911             double dot = 0.0, dist = 0.0;
7912             if ( searchByDir ) { // choose most co-directed link
7913               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7914               linkIsBetter = ( dot > maxDot );
7915             }
7916             else { // choose link with the node closest to bordPos
7917               dist = ( nextXYZ - bordPos ).SquareModulus();
7918               linkIsBetter = ( dist < minDist );
7919             }
7920             if ( linkIsBetter ) {
7921               maxDot = dot;
7922               minDist = dist;
7923               linkID = iLink;
7924               sideNode = n;
7925               sideElem = elem;
7926             }
7927           }
7928         }
7929       } // loop on inverse elements of prevSideNode
7930
7931       if ( !sideNode ) {
7932         MESSAGE(" Can't find path by links of the Side 2 ");
7933         return SEW_BAD_SIDE_NODES;
7934       }
7935       sideNodes.push_back( sideNode );
7936       sideElems.push_back( sideElem );
7937       foundSideLinkIDs.insert ( linkID );
7938       prevSideNode = sideNode;
7939
7940       if ( *nBordIt == theBordLastNode )
7941         searchByDir = false;
7942       else {
7943         // find the next border link to compare with
7944         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7945         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7946         // move to next border node if sideNode is before forward border node (bordPos)
7947         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7948           prevBordNode = *nBordIt;
7949           nBordIt++;
7950           bordPos = nBordXYZ[ *nBordIt ];
7951           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7952           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7953         }
7954       }
7955     }
7956     while ( sideNode != theSideSecondNode );
7957
7958     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7959       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7960       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7961     }
7962   } // end nodes search on the side 2
7963
7964   // ============================
7965   // sew the border to the side 2
7966   // ============================
7967
7968   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
7969   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7970
7971   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7972   if ( toMergeConformal && toCreatePolygons )
7973   {
7974     // do not merge quadrangles if polygons are OK (IPAL0052824)
7975     eIt[0] = eSide[0].begin();
7976     eIt[1] = eSide[1].begin();
7977     bool allQuads[2] = { true, true };
7978     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7979       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7980         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7981     }
7982     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7983   }
7984
7985   TListOfListOfNodes nodeGroupsToMerge;
7986   if (( toMergeConformal ) ||
7987       ( theSideIsFreeBorder && !theSideThirdNode )) {
7988
7989     // all nodes are to be merged
7990
7991     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7992          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7993          nIt[0]++, nIt[1]++ )
7994     {
7995       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7996       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7997       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7998     }
7999   }
8000   else {
8001
8002     // insert new nodes into the border and the side to get equal nb of segments
8003
8004     // get normalized parameters of nodes on the borders
8005     vector< double > param[ 2 ];
8006     param[0].resize( maxNbNodes );
8007     param[1].resize( maxNbNodes );
8008     int iNode, iBord;
8009     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8010       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8011       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8012       const SMDS_MeshNode* nPrev = *nIt;
8013       double bordLength = 0;
8014       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8015         const SMDS_MeshNode* nCur = *nIt;
8016         gp_XYZ segment (nCur->X() - nPrev->X(),
8017                         nCur->Y() - nPrev->Y(),
8018                         nCur->Z() - nPrev->Z());
8019         double segmentLen = segment.Modulus();
8020         bordLength += segmentLen;
8021         param[ iBord ][ iNode ] = bordLength;
8022         nPrev = nCur;
8023       }
8024       // normalize within [0,1]
8025       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8026         param[ iBord ][ iNode ] /= bordLength;
8027       }
8028     }
8029
8030     // loop on border segments
8031     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8032     int i[ 2 ] = { 0, 0 };
8033     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8034     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8035
8036     // element can be split while iterating on border if it has two edges in the border
8037     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8038     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8039
8040     TElemOfNodeListMap insertMap;
8041     TElemOfNodeListMap::iterator insertMapIt;
8042     // insertMap is
8043     // key:   elem to insert nodes into
8044     // value: 2 nodes to insert between + nodes to be inserted
8045     do {
8046       bool next[ 2 ] = { false, false };
8047
8048       // find min adjacent segment length after sewing
8049       double nextParam = 10., prevParam = 0;
8050       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8051         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8052           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8053         if ( i[ iBord ] > 0 )
8054           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8055       }
8056       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8057       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8058       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8059
8060       // choose to insert or to merge nodes
8061       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8062       if ( Abs( du ) <= minSegLen * 0.2 ) {
8063         // merge
8064         // ------
8065         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8066         const SMDS_MeshNode* n0 = *nIt[0];
8067         const SMDS_MeshNode* n1 = *nIt[1];
8068         nodeGroupsToMerge.back().push_back( n1 );
8069         nodeGroupsToMerge.back().push_back( n0 );
8070         // position of node of the border changes due to merge
8071         param[ 0 ][ i[0] ] += du;
8072         // move n1 for the sake of elem shape evaluation during insertion.
8073         // n1 will be removed by MergeNodes() anyway
8074         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8075         next[0] = next[1] = true;
8076       }
8077       else {
8078         // insert
8079         // ------
8080         int intoBord = ( du < 0 ) ? 0 : 1;
8081         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8082         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8083         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8084         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8085         if ( intoBord == 1 ) {
8086           // move node of the border to be on a link of elem of the side
8087           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8088           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8089           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8090           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8091         }
8092         elemReplaceMapIt = elemReplaceMap.find( elem );
8093         if ( elemReplaceMapIt != elemReplaceMap.end() )
8094           elem = elemReplaceMapIt->second;
8095
8096         insertMapIt = insertMap.find( elem );
8097         bool  notFound = ( insertMapIt == insertMap.end() );
8098         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8099         if ( otherLink ) {
8100           // insert into another link of the same element:
8101           // 1. perform insertion into the other link of the elem
8102           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8103           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8104           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8105           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8106           // 2. perform insertion into the link of adjacent faces
8107           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8108             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8109           }
8110           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8111             InsertNodesIntoLink( seg, n12, n22, nodeList );
8112           }
8113           if (toCreatePolyedrs) {
8114             // perform insertion into the links of adjacent volumes
8115             UpdateVolumes(n12, n22, nodeList);
8116           }
8117           // 3. find an element appeared on n1 and n2 after the insertion
8118           insertMap.erase( insertMapIt );
8119           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8120           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8121           elem = elem2;
8122         }
8123         if ( notFound || otherLink ) {
8124           // add element and nodes of the side into the insertMap
8125           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8126           (*insertMapIt).second.push_back( n1 );
8127           (*insertMapIt).second.push_back( n2 );
8128         }
8129         // add node to be inserted into elem
8130         (*insertMapIt).second.push_back( nIns );
8131         next[ 1 - intoBord ] = true;
8132       }
8133
8134       // go to the next segment
8135       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8136         if ( next[ iBord ] ) {
8137           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8138             eIt[ iBord ]++;
8139           nPrev[ iBord ] = *nIt[ iBord ];
8140           nIt[ iBord ]++; i[ iBord ]++;
8141         }
8142       }
8143     }
8144     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8145
8146     // perform insertion of nodes into elements
8147
8148     for (insertMapIt = insertMap.begin();
8149          insertMapIt != insertMap.end();
8150          insertMapIt++ )
8151     {
8152       const SMDS_MeshElement* elem = (*insertMapIt).first;
8153       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8154       if ( nodeList.size() < 3 ) continue;
8155       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8156       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8157
8158       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8159
8160       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8161         InsertNodesIntoLink( seg, n1, n2, nodeList );
8162       }
8163
8164       if ( !theSideIsFreeBorder ) {
8165         // look for and insert nodes into the faces adjacent to elem
8166         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8167           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8168         }
8169       }
8170       if (toCreatePolyedrs) {
8171         // perform insertion into the links of adjacent volumes
8172         UpdateVolumes(n1, n2, nodeList);
8173       }
8174     }
8175   } // end: insert new nodes
8176
8177   MergeNodes ( nodeGroupsToMerge );
8178
8179
8180   // Remove coincident segments
8181
8182   // get new segments
8183   TIDSortedElemSet segments;
8184   SMESH_SequenceOfElemPtr newFaces;
8185   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8186   {
8187     if ( !myLastCreatedElems[i] ) continue;
8188     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8189       segments.insert( segments.end(), myLastCreatedElems[i] );
8190     else
8191       newFaces.push_back( myLastCreatedElems[i] );
8192   }
8193   // get segments adjacent to merged nodes
8194   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8195   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8196   {
8197     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8198     if ( nodes.front()->IsNull() ) continue;
8199     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8200     while ( segIt->more() )
8201       segments.insert( segIt->next() );
8202   }
8203
8204   // find coincident
8205   TListOfListOfElementsID equalGroups;
8206   if ( !segments.empty() )
8207     FindEqualElements( segments, equalGroups );
8208   if ( !equalGroups.empty() )
8209   {
8210     // remove from segments those that will be removed
8211     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8212     for ( ; itGroups != equalGroups.end(); ++itGroups )
8213     {
8214       list< int >& group = *itGroups;
8215       list< int >::iterator id = group.begin();
8216       for ( ++id; id != group.end(); ++id )
8217         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8218           segments.erase( seg );
8219     }
8220     // remove equal segments
8221     MergeElements( equalGroups );
8222
8223     // restore myLastCreatedElems
8224     myLastCreatedElems = newFaces;
8225     TIDSortedElemSet::iterator seg = segments.begin();
8226     for ( ; seg != segments.end(); ++seg )
8227       myLastCreatedElems.push_back( *seg );
8228   }
8229
8230   return aResult;
8231 }
8232
8233 //=======================================================================
8234 //function : InsertNodesIntoLink
8235 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8236 //           and theBetweenNode2 and split theElement
8237 //=======================================================================
8238
8239 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8240                                            const SMDS_MeshNode*        theBetweenNode1,
8241                                            const SMDS_MeshNode*        theBetweenNode2,
8242                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8243                                            const bool                  toCreatePoly)
8244 {
8245   if ( !theElement ) return;
8246
8247   SMESHDS_Mesh *aMesh = GetMeshDS();
8248   vector<const SMDS_MeshElement*> newElems;
8249
8250   if ( theElement->GetType() == SMDSAbs_Edge )
8251   {
8252     theNodesToInsert.push_front( theBetweenNode1 );
8253     theNodesToInsert.push_back ( theBetweenNode2 );
8254     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8255     const SMDS_MeshNode* n1 = *n;
8256     for ( ++n; n != theNodesToInsert.end(); ++n )
8257     {
8258       const SMDS_MeshNode* n2 = *n;
8259       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8260         AddToSameGroups( seg, theElement, aMesh );
8261       else
8262         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8263       n1 = n2;
8264     }
8265     theNodesToInsert.pop_front();
8266     theNodesToInsert.pop_back();
8267
8268     if ( theElement->IsQuadratic() ) // add a not split part
8269     {
8270       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8271                                           theElement->end_nodes() );
8272       int iOther = 0, nbN = nodes.size();
8273       for ( ; iOther < nbN; ++iOther )
8274         if ( nodes[iOther] != theBetweenNode1 &&
8275              nodes[iOther] != theBetweenNode2 )
8276           break;
8277       if      ( iOther == 0 )
8278       {
8279         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8280           AddToSameGroups( seg, theElement, aMesh );
8281         else
8282           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8283       }
8284       else if ( iOther == 2 )
8285       {
8286         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8287           AddToSameGroups( seg, theElement, aMesh );
8288         else
8289           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8290       }
8291     }
8292     // treat new elements
8293     for ( size_t i = 0; i < newElems.size(); ++i )
8294       if ( newElems[i] )
8295       {
8296         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8297         myLastCreatedElems.push_back( newElems[i] );
8298       }
8299     ReplaceElemInGroups( theElement, newElems, aMesh );
8300     aMesh->RemoveElement( theElement );
8301     return;
8302
8303   } // if ( theElement->GetType() == SMDSAbs_Edge )
8304
8305   const SMDS_MeshElement* theFace = theElement;
8306   if ( theFace->GetType() != SMDSAbs_Face ) return;
8307
8308   // find indices of 2 link nodes and of the rest nodes
8309   int iNode = 0, il1, il2, i3, i4;
8310   il1 = il2 = i3 = i4 = -1;
8311   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8312
8313   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8314   while ( nodeIt->more() ) {
8315     const SMDS_MeshNode* n = nodeIt->next();
8316     if ( n == theBetweenNode1 )
8317       il1 = iNode;
8318     else if ( n == theBetweenNode2 )
8319       il2 = iNode;
8320     else if ( i3 < 0 )
8321       i3 = iNode;
8322     else
8323       i4 = iNode;
8324     nodes[ iNode++ ] = n;
8325   }
8326   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8327     return ;
8328
8329   // arrange link nodes to go one after another regarding the face orientation
8330   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8331   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8332   if ( reverse ) {
8333     iNode = il1;
8334     il1 = il2;
8335     il2 = iNode;
8336     aNodesToInsert.reverse();
8337   }
8338   // check that not link nodes of a quadrangles are in good order
8339   int nbFaceNodes = theFace->NbNodes();
8340   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8341     iNode = i3;
8342     i3 = i4;
8343     i4 = iNode;
8344   }
8345
8346   if (toCreatePoly || theFace->IsPoly()) {
8347
8348     iNode = 0;
8349     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8350
8351     // add nodes of face up to first node of link
8352     bool isFLN = false;
8353     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8354     while ( nodeIt->more() && !isFLN ) {
8355       const SMDS_MeshNode* n = nodeIt->next();
8356       poly_nodes[iNode++] = n;
8357       isFLN = ( n == nodes[il1] );
8358     }
8359     // add nodes to insert
8360     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8361     for (; nIt != aNodesToInsert.end(); nIt++) {
8362       poly_nodes[iNode++] = *nIt;
8363     }
8364     // add nodes of face starting from last node of link
8365     while ( nodeIt->more() ) {
8366       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8367       poly_nodes[iNode++] = n;
8368     }
8369
8370     // make a new face
8371     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8372   }
8373
8374   else if ( !theFace->IsQuadratic() )
8375   {
8376     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8377     int nbLinkNodes = 2 + aNodesToInsert.size();
8378     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8379     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8380     linkNodes[ 0 ] = nodes[ il1 ];
8381     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8382     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8383     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8384       linkNodes[ iNode++ ] = *nIt;
8385     }
8386     // decide how to split a quadrangle: compare possible variants
8387     // and choose which of splits to be a quadrangle
8388     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8389     if ( nbFaceNodes == 3 ) {
8390       iBestQuad = nbSplits;
8391       i4 = i3;
8392     }
8393     else if ( nbFaceNodes == 4 ) {
8394       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8395       double aBestRate = DBL_MAX;
8396       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8397         i1 = 0; i2 = 1;
8398         double aBadRate = 0;
8399         // evaluate elements quality
8400         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8401           if ( iSplit == iQuad ) {
8402             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8403                                    linkNodes[ i2++ ],
8404                                    nodes[ i3 ],
8405                                    nodes[ i4 ]);
8406             aBadRate += getBadRate( &quad, aCrit );
8407           }
8408           else {
8409             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8410                                    linkNodes[ i2++ ],
8411                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8412             aBadRate += getBadRate( &tria, aCrit );
8413           }
8414         }
8415         // choice
8416         if ( aBadRate < aBestRate ) {
8417           iBestQuad = iQuad;
8418           aBestRate = aBadRate;
8419         }
8420       }
8421     }
8422
8423     // create new elements
8424     i1 = 0; i2 = 1;
8425     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8426     {
8427       if ( iSplit == iBestQuad )
8428         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8429                                             linkNodes[ i2++ ],
8430                                             nodes[ i3 ],
8431                                             nodes[ i4 ]));
8432       else
8433         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8434                                             linkNodes[ i2++ ],
8435                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8436     }
8437
8438     const SMDS_MeshNode* newNodes[ 4 ];
8439     newNodes[ 0 ] = linkNodes[ i1 ];
8440     newNodes[ 1 ] = linkNodes[ i2 ];
8441     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8442     newNodes[ 3 ] = nodes[ i4 ];
8443     if (iSplit == iBestQuad)
8444       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8445     else
8446       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8447
8448   } // end if(!theFace->IsQuadratic())
8449
8450   else { // theFace is quadratic
8451     // we have to split theFace on simple triangles and one simple quadrangle
8452     int tmp = il1/2;
8453     int nbshift = tmp*2;
8454     // shift nodes in nodes[] by nbshift
8455     int i,j;
8456     for(i=0; i<nbshift; i++) {
8457       const SMDS_MeshNode* n = nodes[0];
8458       for(j=0; j<nbFaceNodes-1; j++) {
8459         nodes[j] = nodes[j+1];
8460       }
8461       nodes[nbFaceNodes-1] = n;
8462     }
8463     il1 = il1 - nbshift;
8464     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8465     //   n0      n1     n2    n0      n1     n2
8466     //     +-----+-----+        +-----+-----+
8467     //      \         /         |           |
8468     //       \       /          |           |
8469     //      n5+     +n3       n7+           +n3
8470     //         \   /            |           |
8471     //          \ /             |           |
8472     //           +              +-----+-----+
8473     //           n4           n6      n5     n4
8474
8475     // create new elements
8476     int n1,n2,n3;
8477     if ( nbFaceNodes == 6 ) { // quadratic triangle
8478       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8479       if ( theFace->IsMediumNode(nodes[il1]) ) {
8480         // create quadrangle
8481         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8482         n1 = 1;
8483         n2 = 2;
8484         n3 = 3;
8485       }
8486       else {
8487         // create quadrangle
8488         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8489         n1 = 0;
8490         n2 = 1;
8491         n3 = 5;
8492       }
8493     }
8494     else { // nbFaceNodes==8 - quadratic quadrangle
8495       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8496       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8497       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8498       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8499         // create quadrangle
8500         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8501         n1 = 1;
8502         n2 = 2;
8503         n3 = 3;
8504       }
8505       else {
8506         // create quadrangle
8507         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8508         n1 = 0;
8509         n2 = 1;
8510         n3 = 7;
8511       }
8512     }
8513     // create needed triangles using n1,n2,n3 and inserted nodes
8514     int nbn = 2 + aNodesToInsert.size();
8515     vector<const SMDS_MeshNode*> aNodes(nbn);
8516     aNodes[0    ] = nodes[n1];
8517     aNodes[nbn-1] = nodes[n2];
8518     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8519     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8520       aNodes[iNode++] = *nIt;
8521     }
8522     for ( i = 1; i < nbn; i++ )
8523       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8524   }
8525
8526   // remove the old face
8527   for ( size_t i = 0; i < newElems.size(); ++i )
8528     if ( newElems[i] )
8529     {
8530       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8531       myLastCreatedElems.push_back( newElems[i] );
8532     }
8533   ReplaceElemInGroups( theFace, newElems, aMesh );
8534   aMesh->RemoveElement(theFace);
8535
8536 } // InsertNodesIntoLink()
8537
8538 //=======================================================================
8539 //function : UpdateVolumes
8540 //purpose  :
8541 //=======================================================================
8542
8543 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8544                                       const SMDS_MeshNode*        theBetweenNode2,
8545                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8546 {
8547   ClearLastCreated();
8548
8549   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8550   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8551     const SMDS_MeshElement* elem = invElemIt->next();
8552
8553     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8554     SMDS_VolumeTool aVolume (elem);
8555     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8556       continue;
8557
8558     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8559     int iface, nbFaces = aVolume.NbFaces();
8560     vector<const SMDS_MeshNode *> poly_nodes;
8561     vector<int> quantities (nbFaces);
8562
8563     for (iface = 0; iface < nbFaces; iface++) {
8564       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8565       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8566       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8567
8568       for (int inode = 0; inode < nbFaceNodes; inode++) {
8569         poly_nodes.push_back(faceNodes[inode]);
8570
8571         if (nbInserted == 0) {
8572           if (faceNodes[inode] == theBetweenNode1) {
8573             if (faceNodes[inode + 1] == theBetweenNode2) {
8574               nbInserted = theNodesToInsert.size();
8575
8576               // add nodes to insert
8577               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8578               for (; nIt != theNodesToInsert.end(); nIt++) {
8579                 poly_nodes.push_back(*nIt);
8580               }
8581             }
8582           }
8583           else if (faceNodes[inode] == theBetweenNode2) {
8584             if (faceNodes[inode + 1] == theBetweenNode1) {
8585               nbInserted = theNodesToInsert.size();
8586
8587               // add nodes to insert in reversed order
8588               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8589               nIt--;
8590               for (; nIt != theNodesToInsert.begin(); nIt--) {
8591                 poly_nodes.push_back(*nIt);
8592               }
8593               poly_nodes.push_back(*nIt);
8594             }
8595           }
8596           else {
8597           }
8598         }
8599       }
8600       quantities[iface] = nbFaceNodes + nbInserted;
8601     }
8602
8603     // Replace the volume
8604     SMESHDS_Mesh *aMesh = GetMeshDS();
8605
8606     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8607     {
8608       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8609       myLastCreatedElems.push_back( newElem );
8610       ReplaceElemInGroups( elem, newElem, aMesh );
8611     }
8612     aMesh->RemoveElement( elem );
8613   }
8614 }
8615
8616 namespace
8617 {
8618   //================================================================================
8619   /*!
8620    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8621    */
8622   //================================================================================
8623
8624   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8625                            vector<const SMDS_MeshNode *> & nodes,
8626                            vector<int> &                   nbNodeInFaces )
8627   {
8628     nodes.clear();
8629     nbNodeInFaces.clear();
8630     SMDS_VolumeTool vTool ( elem );
8631     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8632     {
8633       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8634       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8635       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8636     }
8637   }
8638 }
8639
8640 //=======================================================================
8641 /*!
8642  * \brief Convert elements contained in a sub-mesh to quadratic
8643  * \return int - nb of checked elements
8644  */
8645 //=======================================================================
8646
8647 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8648                                              SMESH_MesherHelper& theHelper,
8649                                              const bool          theForce3d)
8650 {
8651   //MESSAGE("convertElemToQuadratic");
8652   int nbElem = 0;
8653   if( !theSm ) return nbElem;
8654
8655   vector<int> nbNodeInFaces;
8656   vector<const SMDS_MeshNode *> nodes;
8657   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8658   while(ElemItr->more())
8659   {
8660     nbElem++;
8661     const SMDS_MeshElement* elem = ElemItr->next();
8662     if( !elem ) continue;
8663
8664     // analyse a necessity of conversion
8665     const SMDSAbs_ElementType aType = elem->GetType();
8666     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8667       continue;
8668     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8669     bool hasCentralNodes = false;
8670     if ( elem->IsQuadratic() )
8671     {
8672       bool alreadyOK;
8673       switch ( aGeomType ) {
8674       case SMDSEntity_Quad_Triangle:
8675       case SMDSEntity_Quad_Quadrangle:
8676       case SMDSEntity_Quad_Hexa:
8677       case SMDSEntity_Quad_Penta:
8678         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8679
8680       case SMDSEntity_BiQuad_Triangle:
8681       case SMDSEntity_BiQuad_Quadrangle:
8682       case SMDSEntity_TriQuad_Hexa:
8683       case SMDSEntity_BiQuad_Penta:
8684         alreadyOK = theHelper.GetIsBiQuadratic();
8685         hasCentralNodes = true;
8686         break;
8687       default:
8688         alreadyOK = true;
8689       }
8690       // take into account already present medium nodes
8691       switch ( aType ) {
8692       case SMDSAbs_Volume:
8693         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8694       case SMDSAbs_Face:
8695         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8696       case SMDSAbs_Edge:
8697         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8698       default:;
8699       }
8700       if ( alreadyOK )
8701         continue;
8702     }
8703     // get elem data needed to re-create it
8704     //
8705     const int id      = elem->GetID();
8706     const int nbNodes = elem->NbCornerNodes();
8707     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8708     if ( aGeomType == SMDSEntity_Polyhedra )
8709       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8710     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8711       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8712
8713     // remove a linear element
8714     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8715
8716     // remove central nodes of biquadratic elements (biquad->quad conversion)
8717     if ( hasCentralNodes )
8718       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8719         if ( nodes[i]->NbInverseElements() == 0 )
8720           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8721
8722     const SMDS_MeshElement* NewElem = 0;
8723
8724     switch( aType )
8725     {
8726     case SMDSAbs_Edge :
8727     {
8728       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8729       break;
8730     }
8731     case SMDSAbs_Face :
8732     {
8733       switch(nbNodes)
8734       {
8735       case 3:
8736         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8737         break;
8738       case 4:
8739         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8740         break;
8741       default:
8742         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8743       }
8744       break;
8745     }
8746     case SMDSAbs_Volume :
8747     {
8748       switch( aGeomType )
8749       {
8750       case SMDSEntity_Tetra:
8751         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8752         break;
8753       case SMDSEntity_Pyramid:
8754         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8755         break;
8756       case SMDSEntity_Penta:
8757       case SMDSEntity_Quad_Penta:
8758       case SMDSEntity_BiQuad_Penta:
8759         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8760         break;
8761       case SMDSEntity_Hexa:
8762       case SMDSEntity_Quad_Hexa:
8763       case SMDSEntity_TriQuad_Hexa:
8764         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8765                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8766         break;
8767       case SMDSEntity_Hexagonal_Prism:
8768       default:
8769         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8770       }
8771       break;
8772     }
8773     default :
8774       continue;
8775     }
8776     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8777     if( NewElem && NewElem->getshapeId() < 1 )
8778       theSm->AddElement( NewElem );
8779   }
8780   return nbElem;
8781 }
8782 //=======================================================================
8783 //function : ConvertToQuadratic
8784 //purpose  :
8785 //=======================================================================
8786
8787 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8788 {
8789   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8790   SMESHDS_Mesh* meshDS = GetMeshDS();
8791
8792   SMESH_MesherHelper aHelper(*myMesh);
8793
8794   aHelper.SetIsQuadratic( true );
8795   aHelper.SetIsBiQuadratic( theToBiQuad );
8796   aHelper.SetElementsOnShape(true);
8797   aHelper.ToFixNodeParameters( true );
8798
8799   // convert elements assigned to sub-meshes
8800   int nbCheckedElems = 0;
8801   if ( myMesh->HasShapeToMesh() )
8802   {
8803     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8804     {
8805       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8806       while ( smIt->more() ) {
8807         SMESH_subMesh* sm = smIt->next();
8808         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8809           aHelper.SetSubShape( sm->GetSubShape() );
8810           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8811         }
8812       }
8813     }
8814   }
8815
8816   // convert elements NOT assigned to sub-meshes
8817   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8818   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8819   {
8820     aHelper.SetElementsOnShape(false);
8821     SMESHDS_SubMesh *smDS = 0;
8822
8823     // convert edges
8824     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8825     while( aEdgeItr->more() )
8826     {
8827       const SMDS_MeshEdge* edge = aEdgeItr->next();
8828       if ( !edge->IsQuadratic() )
8829       {
8830         int                  id = edge->GetID();
8831         const SMDS_MeshNode* n1 = edge->GetNode(0);
8832         const SMDS_MeshNode* n2 = edge->GetNode(1);
8833
8834         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8835
8836         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8837         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8838       }
8839       else
8840       {
8841         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8842       }
8843     }
8844
8845     // convert faces
8846     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8847     while( aFaceItr->more() )
8848     {
8849       const SMDS_MeshFace* face = aFaceItr->next();
8850       if ( !face ) continue;
8851       
8852       const SMDSAbs_EntityType type = face->GetEntityType();
8853       bool alreadyOK;
8854       switch( type )
8855       {
8856       case SMDSEntity_Quad_Triangle:
8857       case SMDSEntity_Quad_Quadrangle:
8858         alreadyOK = !theToBiQuad;
8859         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8860         break;
8861       case SMDSEntity_BiQuad_Triangle:
8862       case SMDSEntity_BiQuad_Quadrangle:
8863         alreadyOK = theToBiQuad;
8864         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8865         break;
8866       default: alreadyOK = false;
8867       }
8868       if ( alreadyOK )
8869         continue;
8870
8871       const int id = face->GetID();
8872       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8873
8874       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8875
8876       SMDS_MeshFace * NewFace = 0;
8877       switch( type )
8878       {
8879       case SMDSEntity_Triangle:
8880       case SMDSEntity_Quad_Triangle:
8881       case SMDSEntity_BiQuad_Triangle:
8882         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8883         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8884           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8885         break;
8886
8887       case SMDSEntity_Quadrangle:
8888       case SMDSEntity_Quad_Quadrangle:
8889       case SMDSEntity_BiQuad_Quadrangle:
8890         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8891         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8892           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8893         break;
8894
8895       default:;
8896         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8897       }
8898       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8899     }
8900
8901     // convert volumes
8902     vector<int> nbNodeInFaces;
8903     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8904     while(aVolumeItr->more())
8905     {
8906       const SMDS_MeshVolume* volume = aVolumeItr->next();
8907       if ( !volume ) continue;
8908
8909       const SMDSAbs_EntityType type = volume->GetEntityType();
8910       if ( volume->IsQuadratic() )
8911       {
8912         bool alreadyOK;
8913         switch ( type )
8914         {
8915         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8916         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8917         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8918         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8919         default:                      alreadyOK = true;
8920         }
8921         if ( alreadyOK )
8922         {
8923           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8924           continue;
8925         }
8926       }
8927       const int id = volume->GetID();
8928       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8929       if ( type == SMDSEntity_Polyhedra )
8930         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8931       else if ( type == SMDSEntity_Hexagonal_Prism )
8932         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8933
8934       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8935
8936       SMDS_MeshVolume * NewVolume = 0;
8937       switch ( type )
8938       {
8939       case SMDSEntity_Tetra:
8940         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8941         break;
8942       case SMDSEntity_Hexa:
8943       case SMDSEntity_Quad_Hexa:
8944       case SMDSEntity_TriQuad_Hexa:
8945         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8946                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8947         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8948           if ( nodes[i]->NbInverseElements() == 0 )
8949             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8950         break;
8951       case SMDSEntity_Pyramid:
8952         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8953                                       nodes[3], nodes[4], id, theForce3d);
8954         break;
8955       case SMDSEntity_Penta:
8956       case SMDSEntity_Quad_Penta:
8957       case SMDSEntity_BiQuad_Penta:
8958         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8959                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8960         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8961           if ( nodes[i]->NbInverseElements() == 0 )
8962             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8963         break;
8964       case SMDSEntity_Hexagonal_Prism:
8965       default:
8966         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8967       }
8968       ReplaceElemInGroups(volume, NewVolume, meshDS);
8969     }
8970   }
8971
8972   if ( !theForce3d )
8973   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8974     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8975     // aHelper.FixQuadraticElements(myError);
8976     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8977   }
8978 }
8979
8980 //================================================================================
8981 /*!
8982  * \brief Makes given elements quadratic
8983  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8984  *  \param theElements - elements to make quadratic
8985  */
8986 //================================================================================
8987
8988 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8989                                           TIDSortedElemSet& theElements,
8990                                           const bool        theToBiQuad)
8991 {
8992   if ( theElements.empty() ) return;
8993
8994   // we believe that all theElements are of the same type
8995   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8996
8997   // get all nodes shared by theElements
8998   TIDSortedNodeSet allNodes;
8999   TIDSortedElemSet::iterator eIt = theElements.begin();
9000   for ( ; eIt != theElements.end(); ++eIt )
9001     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9002
9003   // complete theElements with elements of lower dim whose all nodes are in allNodes
9004
9005   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9006   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9007   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9008   for ( ; nIt != allNodes.end(); ++nIt )
9009   {
9010     const SMDS_MeshNode* n = *nIt;
9011     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9012     while ( invIt->more() )
9013     {
9014       const SMDS_MeshElement*      e = invIt->next();
9015       const SMDSAbs_ElementType type = e->GetType();
9016       if ( e->IsQuadratic() )
9017       {
9018         quadAdjacentElems[ type ].insert( e );
9019
9020         bool alreadyOK;
9021         switch ( e->GetEntityType() ) {
9022         case SMDSEntity_Quad_Triangle:
9023         case SMDSEntity_Quad_Quadrangle:
9024         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9025         case SMDSEntity_BiQuad_Triangle:
9026         case SMDSEntity_BiQuad_Quadrangle:
9027         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9028         default:                           alreadyOK = true;
9029         }
9030         if ( alreadyOK )
9031           continue;
9032       }
9033       if ( type >= elemType )
9034         continue; // same type or more complex linear element
9035
9036       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9037         continue; // e is already checked
9038
9039       // check nodes
9040       bool allIn = true;
9041       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9042       while ( nodeIt->more() && allIn )
9043         allIn = allNodes.count( nodeIt->next() );
9044       if ( allIn )
9045         theElements.insert(e );
9046     }
9047   }
9048
9049   SMESH_MesherHelper helper(*myMesh);
9050   helper.SetIsQuadratic( true );
9051   helper.SetIsBiQuadratic( theToBiQuad );
9052
9053   // add links of quadratic adjacent elements to the helper
9054
9055   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9056     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9057           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9058     {
9059       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9060     }
9061   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9062     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9063           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9064     {
9065       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9066     }
9067   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9068     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9069           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9070     {
9071       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9072     }
9073
9074   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9075
9076   SMESHDS_Mesh*  meshDS = GetMeshDS();
9077   SMESHDS_SubMesh* smDS = 0;
9078   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9079   {
9080     const SMDS_MeshElement* elem = *eIt;
9081
9082     bool alreadyOK;
9083     int nbCentralNodes = 0;
9084     switch ( elem->GetEntityType() ) {
9085       // linear convertible
9086     case SMDSEntity_Edge:
9087     case SMDSEntity_Triangle:
9088     case SMDSEntity_Quadrangle:
9089     case SMDSEntity_Tetra:
9090     case SMDSEntity_Pyramid:
9091     case SMDSEntity_Hexa:
9092     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9093       // quadratic that can become bi-quadratic
9094     case SMDSEntity_Quad_Triangle:
9095     case SMDSEntity_Quad_Quadrangle:
9096     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9097       // bi-quadratic
9098     case SMDSEntity_BiQuad_Triangle:
9099     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9100     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9101       // the rest
9102     default:                           alreadyOK = true;
9103     }
9104     if ( alreadyOK ) continue;
9105
9106     const SMDSAbs_ElementType type = elem->GetType();
9107     const int                   id = elem->GetID();
9108     const int              nbNodes = elem->NbCornerNodes();
9109     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9110
9111     helper.SetSubShape( elem->getshapeId() );
9112
9113     if ( !smDS || !smDS->Contains( elem ))
9114       smDS = meshDS->MeshElements( elem->getshapeId() );
9115     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9116
9117     SMDS_MeshElement * newElem = 0;
9118     switch( nbNodes )
9119     {
9120     case 4: // cases for most frequently used element types go first (for optimization)
9121       if ( type == SMDSAbs_Volume )
9122         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9123       else
9124         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9125       break;
9126     case 8:
9127       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9128                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9129       break;
9130     case 3:
9131       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9132       break;
9133     case 2:
9134       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9135       break;
9136     case 5:
9137       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9138                                  nodes[4], id, theForce3d);
9139       break;
9140     case 6:
9141       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9142                                  nodes[4], nodes[5], id, theForce3d);
9143       break;
9144     default:;
9145     }
9146     ReplaceElemInGroups( elem, newElem, meshDS);
9147     if( newElem && smDS )
9148       smDS->AddElement( newElem );
9149
9150     // remove central nodes
9151     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9152       if ( nodes[i]->NbInverseElements() == 0 )
9153         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9154
9155   } // loop on theElements
9156
9157   if ( !theForce3d )
9158   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9159     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9160     // helper.FixQuadraticElements( myError );
9161     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9162   }
9163 }
9164
9165 //=======================================================================
9166 /*!
9167  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9168  * \return int - nb of checked elements
9169  */
9170 //=======================================================================
9171
9172 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9173                                      SMDS_ElemIteratorPtr theItr,
9174                                      const int            theShapeID)
9175 {
9176   int nbElem = 0;
9177   SMESHDS_Mesh* meshDS = GetMeshDS();
9178   ElemFeatures elemType;
9179   vector<const SMDS_MeshNode *> nodes;
9180
9181   while( theItr->more() )
9182   {
9183     const SMDS_MeshElement* elem = theItr->next();
9184     nbElem++;
9185     if( elem && elem->IsQuadratic())
9186     {
9187       // get elem data
9188       int nbCornerNodes = elem->NbCornerNodes();
9189       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9190
9191       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9192
9193       //remove a quadratic element
9194       if ( !theSm || !theSm->Contains( elem ))
9195         theSm = meshDS->MeshElements( elem->getshapeId() );
9196       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9197
9198       // remove medium nodes
9199       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9200         if ( nodes[i]->NbInverseElements() == 0 )
9201           meshDS->RemoveFreeNode( nodes[i], theSm );
9202
9203       // add a linear element
9204       nodes.resize( nbCornerNodes );
9205       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9206       ReplaceElemInGroups(elem, newElem, meshDS);
9207       if( theSm && newElem )
9208         theSm->AddElement( newElem );
9209     }
9210   }
9211   return nbElem;
9212 }
9213
9214 //=======================================================================
9215 //function : ConvertFromQuadratic
9216 //purpose  :
9217 //=======================================================================
9218
9219 bool SMESH_MeshEditor::ConvertFromQuadratic()
9220 {
9221   int nbCheckedElems = 0;
9222   if ( myMesh->HasShapeToMesh() )
9223   {
9224     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9225     {
9226       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9227       while ( smIt->more() ) {
9228         SMESH_subMesh* sm = smIt->next();
9229         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9230           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9231       }
9232     }
9233   }
9234
9235   int totalNbElems =
9236     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9237   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9238   {
9239     SMESHDS_SubMesh *aSM = 0;
9240     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9241   }
9242
9243   return true;
9244 }
9245
9246 namespace
9247 {
9248   //================================================================================
9249   /*!
9250    * \brief Return true if all medium nodes of the element are in the node set
9251    */
9252   //================================================================================
9253
9254   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9255   {
9256     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9257       if ( !nodeSet.count( elem->GetNode(i) ))
9258         return false;
9259     return true;
9260   }
9261 }
9262
9263 //================================================================================
9264 /*!
9265  * \brief Makes given elements linear
9266  */
9267 //================================================================================
9268
9269 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9270 {
9271   if ( theElements.empty() ) return;
9272
9273   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9274   set<int> mediumNodeIDs;
9275   TIDSortedElemSet::iterator eIt = theElements.begin();
9276   for ( ; eIt != theElements.end(); ++eIt )
9277   {
9278     const SMDS_MeshElement* e = *eIt;
9279     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9280       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9281   }
9282
9283   // replace given elements by linear ones
9284   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9285   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9286
9287   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9288   // except those elements sharing medium nodes of quadratic element whose medium nodes
9289   // are not all in mediumNodeIDs
9290
9291   // get remaining medium nodes
9292   TIDSortedNodeSet mediumNodes;
9293   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9294   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9295     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9296       mediumNodes.insert( mediumNodes.end(), n );
9297
9298   // find more quadratic elements to convert
9299   TIDSortedElemSet moreElemsToConvert;
9300   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9301   for ( ; nIt != mediumNodes.end(); ++nIt )
9302   {
9303     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9304     while ( invIt->more() )
9305     {
9306       const SMDS_MeshElement* e = invIt->next();
9307       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9308       {
9309         // find a more complex element including e and
9310         // whose medium nodes are not in mediumNodes
9311         bool complexFound = false;
9312         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9313         {
9314           SMDS_ElemIteratorPtr invIt2 =
9315             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9316           while ( invIt2->more() )
9317           {
9318             const SMDS_MeshElement* eComplex = invIt2->next();
9319             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9320             {
9321               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9322               if ( nbCommonNodes == e->NbNodes())
9323               {
9324                 complexFound = true;
9325                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9326                 break;
9327               }
9328             }
9329           }
9330         }
9331         if ( !complexFound )
9332           moreElemsToConvert.insert( e );
9333       }
9334     }
9335   }
9336   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9337   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9338 }
9339
9340 //=======================================================================
9341 //function : SewSideElements
9342 //purpose  :
9343 //=======================================================================
9344
9345 SMESH_MeshEditor::Sew_Error
9346 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9347                                    TIDSortedElemSet&    theSide2,
9348                                    const SMDS_MeshNode* theFirstNode1,
9349                                    const SMDS_MeshNode* theFirstNode2,
9350                                    const SMDS_MeshNode* theSecondNode1,
9351                                    const SMDS_MeshNode* theSecondNode2)
9352 {
9353   ClearLastCreated();
9354
9355   if ( theSide1.size() != theSide2.size() )
9356     return SEW_DIFF_NB_OF_ELEMENTS;
9357
9358   Sew_Error aResult = SEW_OK;
9359   // Algo:
9360   // 1. Build set of faces representing each side
9361   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9362   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9363
9364   // =======================================================================
9365   // 1. Build set of faces representing each side:
9366   // =======================================================================
9367   // a. build set of nodes belonging to faces
9368   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9369   // c. create temporary faces representing side of volumes if correspondent
9370   //    face does not exist
9371
9372   SMESHDS_Mesh* aMesh = GetMeshDS();
9373   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9374   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9375   TIDSortedElemSet             faceSet1, faceSet2;
9376   set<const SMDS_MeshElement*> volSet1,  volSet2;
9377   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9378   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9379   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9380   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9381   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9382   int iSide, iFace, iNode;
9383
9384   list<const SMDS_MeshElement* > tempFaceList;
9385   for ( iSide = 0; iSide < 2; iSide++ ) {
9386     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9387     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9388     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9389     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9390     set<const SMDS_MeshElement*>::iterator vIt;
9391     TIDSortedElemSet::iterator eIt;
9392     set<const SMDS_MeshNode*>::iterator    nIt;
9393
9394     // check that given nodes belong to given elements
9395     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9396     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9397     int firstIndex = -1, secondIndex = -1;
9398     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9399       const SMDS_MeshElement* elem = *eIt;
9400       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9401       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9402       if ( firstIndex > -1 && secondIndex > -1 ) break;
9403     }
9404     if ( firstIndex < 0 || secondIndex < 0 ) {
9405       // we can simply return until temporary faces created
9406       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9407     }
9408
9409     // -----------------------------------------------------------
9410     // 1a. Collect nodes of existing faces
9411     //     and build set of face nodes in order to detect missing
9412     //     faces corresponding to sides of volumes
9413     // -----------------------------------------------------------
9414
9415     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9416
9417     // loop on the given element of a side
9418     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9419       //const SMDS_MeshElement* elem = *eIt;
9420       const SMDS_MeshElement* elem = *eIt;
9421       if ( elem->GetType() == SMDSAbs_Face ) {
9422         faceSet->insert( elem );
9423         set <const SMDS_MeshNode*> faceNodeSet;
9424         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9425         while ( nodeIt->more() ) {
9426           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9427           nodeSet->insert( n );
9428           faceNodeSet.insert( n );
9429         }
9430         setOfFaceNodeSet.insert( faceNodeSet );
9431       }
9432       else if ( elem->GetType() == SMDSAbs_Volume )
9433         volSet->insert( elem );
9434     }
9435     // ------------------------------------------------------------------------------
9436     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9437     // ------------------------------------------------------------------------------
9438
9439     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9440       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9441       while ( fIt->more() ) { // loop on faces sharing a node
9442         const SMDS_MeshElement* f = fIt->next();
9443         if ( faceSet->find( f ) == faceSet->end() ) {
9444           // check if all nodes are in nodeSet and
9445           // complete setOfFaceNodeSet if they are
9446           set <const SMDS_MeshNode*> faceNodeSet;
9447           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9448           bool allInSet = true;
9449           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9450             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9451             if ( nodeSet->find( n ) == nodeSet->end() )
9452               allInSet = false;
9453             else
9454               faceNodeSet.insert( n );
9455           }
9456           if ( allInSet ) {
9457             faceSet->insert( f );
9458             setOfFaceNodeSet.insert( faceNodeSet );
9459           }
9460         }
9461       }
9462     }
9463
9464     // -------------------------------------------------------------------------
9465     // 1c. Create temporary faces representing sides of volumes if correspondent
9466     //     face does not exist
9467     // -------------------------------------------------------------------------
9468
9469     if ( !volSet->empty() ) {
9470       //int nodeSetSize = nodeSet->size();
9471
9472       // loop on given volumes
9473       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9474         SMDS_VolumeTool vol (*vIt);
9475         // loop on volume faces: find free faces
9476         // --------------------------------------
9477         list<const SMDS_MeshElement* > freeFaceList;
9478         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9479           if ( !vol.IsFreeFace( iFace ))
9480             continue;
9481           // check if there is already a face with same nodes in a face set
9482           const SMDS_MeshElement* aFreeFace = 0;
9483           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9484           int nbNodes = vol.NbFaceNodes( iFace );
9485           set <const SMDS_MeshNode*> faceNodeSet;
9486           vol.GetFaceNodes( iFace, faceNodeSet );
9487           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9488           if ( isNewFace ) {
9489             // no such a face is given but it still can exist, check it
9490             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9491             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9492           }
9493           if ( !aFreeFace ) {
9494             // create a temporary face
9495             if ( nbNodes == 3 ) {
9496               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9497               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9498             }
9499             else if ( nbNodes == 4 ) {
9500               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9501               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9502             }
9503             else {
9504               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9505               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9506               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9507             }
9508             if ( aFreeFace )
9509               tempFaceList.push_back( aFreeFace );
9510           }
9511
9512           if ( aFreeFace )
9513             freeFaceList.push_back( aFreeFace );
9514
9515         } // loop on faces of a volume
9516
9517         // choose one of several free faces of a volume
9518         // --------------------------------------------
9519         if ( freeFaceList.size() > 1 ) {
9520           // choose a face having max nb of nodes shared by other elems of a side
9521           int maxNbNodes = -1;
9522           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9523           while ( fIt != freeFaceList.end() ) { // loop on free faces
9524             int nbSharedNodes = 0;
9525             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9526             while ( nodeIt->more() ) { // loop on free face nodes
9527               const SMDS_MeshNode* n =
9528                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9529               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9530               while ( invElemIt->more() ) {
9531                 const SMDS_MeshElement* e = invElemIt->next();
9532                 nbSharedNodes += faceSet->count( e );
9533                 nbSharedNodes += elemSet->count( e );
9534               }
9535             }
9536             if ( nbSharedNodes > maxNbNodes ) {
9537               maxNbNodes = nbSharedNodes;
9538               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9539             }
9540             else if ( nbSharedNodes == maxNbNodes ) {
9541               fIt++;
9542             }
9543             else {
9544               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9545             }
9546           }
9547           if ( freeFaceList.size() > 1 )
9548           {
9549             // could not choose one face, use another way
9550             // choose a face most close to the bary center of the opposite side
9551             gp_XYZ aBC( 0., 0., 0. );
9552             set <const SMDS_MeshNode*> addedNodes;
9553             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9554             eIt = elemSet2->begin();
9555             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9556               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9557               while ( nodeIt->more() ) { // loop on free face nodes
9558                 const SMDS_MeshNode* n =
9559                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560                 if ( addedNodes.insert( n ).second )
9561                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9562               }
9563             }
9564             aBC /= addedNodes.size();
9565             double minDist = DBL_MAX;
9566             fIt = freeFaceList.begin();
9567             while ( fIt != freeFaceList.end() ) { // loop on free faces
9568               double dist = 0;
9569               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9570               while ( nodeIt->more() ) { // loop on free face nodes
9571                 const SMDS_MeshNode* n =
9572                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9573                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9574                 dist += ( aBC - p ).SquareModulus();
9575               }
9576               if ( dist < minDist ) {
9577                 minDist = dist;
9578                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9579               }
9580               else
9581                 fIt = freeFaceList.erase( fIt++ );
9582             }
9583           }
9584         } // choose one of several free faces of a volume
9585
9586         if ( freeFaceList.size() == 1 ) {
9587           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9588           faceSet->insert( aFreeFace );
9589           // complete a node set with nodes of a found free face
9590           //           for ( iNode = 0; iNode < ; iNode++ )
9591           //             nodeSet->insert( fNodes[ iNode ] );
9592         }
9593
9594       } // loop on volumes of a side
9595
9596       //       // complete a set of faces if new nodes in a nodeSet appeared
9597       //       // ----------------------------------------------------------
9598       //       if ( nodeSetSize != nodeSet->size() ) {
9599       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9600       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9601       //           while ( fIt->more() ) { // loop on faces sharing a node
9602       //             const SMDS_MeshElement* f = fIt->next();
9603       //             if ( faceSet->find( f ) == faceSet->end() ) {
9604       //               // check if all nodes are in nodeSet and
9605       //               // complete setOfFaceNodeSet if they are
9606       //               set <const SMDS_MeshNode*> faceNodeSet;
9607       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9608       //               bool allInSet = true;
9609       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9610       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9611       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9612       //                   allInSet = false;
9613       //                 else
9614       //                   faceNodeSet.insert( n );
9615       //               }
9616       //               if ( allInSet ) {
9617       //                 faceSet->insert( f );
9618       //                 setOfFaceNodeSet.insert( faceNodeSet );
9619       //               }
9620       //             }
9621       //           }
9622       //         }
9623       //       }
9624     } // Create temporary faces, if there are volumes given
9625   } // loop on sides
9626
9627   if ( faceSet1.size() != faceSet2.size() ) {
9628     // delete temporary faces: they are in reverseElements of actual nodes
9629     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9630     //    while ( tmpFaceIt->more() )
9631     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9632     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9633     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9634     //      aMesh->RemoveElement(*tmpFaceIt);
9635     MESSAGE("Diff nb of faces");
9636     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9637   }
9638
9639   // ============================================================
9640   // 2. Find nodes to merge:
9641   //              bind a node to remove to a node to put instead
9642   // ============================================================
9643
9644   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9645   if ( theFirstNode1 != theFirstNode2 )
9646     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9647   if ( theSecondNode1 != theSecondNode2 )
9648     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9649
9650   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9651   set< long > linkIdSet; // links to process
9652   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9653
9654   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9655   list< NLink > linkList[2];
9656   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9657   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9658   // loop on links in linkList; find faces by links and append links
9659   // of the found faces to linkList
9660   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9661   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9662   {
9663     NLink link[] = { *linkIt[0], *linkIt[1] };
9664     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9665     if ( !linkIdSet.count( linkID ) )
9666       continue;
9667
9668     // by links, find faces in the face sets,
9669     // and find indices of link nodes in the found faces;
9670     // in a face set, there is only one or no face sharing a link
9671     // ---------------------------------------------------------------
9672
9673     const SMDS_MeshElement* face[] = { 0, 0 };
9674     vector<const SMDS_MeshNode*> fnodes[2];
9675     int iLinkNode[2][2];
9676     TIDSortedElemSet avoidSet;
9677     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9678       const SMDS_MeshNode* n1 = link[iSide].first;
9679       const SMDS_MeshNode* n2 = link[iSide].second;
9680       //cout << "Side " << iSide << " ";
9681       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9682       // find a face by two link nodes
9683       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9684                                                       *faceSetPtr[ iSide ], avoidSet,
9685                                                       &iLinkNode[iSide][0],
9686                                                       &iLinkNode[iSide][1] );
9687       if ( face[ iSide ])
9688       {
9689         //cout << " F " << face[ iSide]->GetID() <<endl;
9690         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9691         // put face nodes to fnodes
9692         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9693         fnodes[ iSide ].assign( nIt, nEnd );
9694         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9695       }
9696     }
9697
9698     // check similarity of elements of the sides
9699     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9700       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9701       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9702         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9703       }
9704       else {
9705         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9706       }
9707       break; // do not return because it's necessary to remove tmp faces
9708     }
9709
9710     // set nodes to merge
9711     // -------------------
9712
9713     if ( face[0] && face[1] )  {
9714       const int nbNodes = face[0]->NbNodes();
9715       if ( nbNodes != face[1]->NbNodes() ) {
9716         MESSAGE("Diff nb of face nodes");
9717         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9718         break; // do not return because it s necessary to remove tmp faces
9719       }
9720       bool reverse[] = { false, false }; // order of nodes in the link
9721       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9722         // analyse link orientation in faces
9723         int i1 = iLinkNode[ iSide ][ 0 ];
9724         int i2 = iLinkNode[ iSide ][ 1 ];
9725         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9726       }
9727       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9728       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9729       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9730       {
9731         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9732                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9733       }
9734
9735       // add other links of the faces to linkList
9736       // -----------------------------------------
9737
9738       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9739         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9740         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9741         if ( !iter_isnew.second ) { // already in a set: no need to process
9742           linkIdSet.erase( iter_isnew.first );
9743         }
9744         else // new in set == encountered for the first time: add
9745         {
9746           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9747           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9748           linkList[0].push_back ( NLink( n1, n2 ));
9749           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9750         }
9751       }
9752     } // 2 faces found
9753
9754     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9755       break;
9756
9757   } // loop on link lists
9758
9759   if ( aResult == SEW_OK &&
9760        ( //linkIt[0] != linkList[0].end() ||
9761         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9762     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9763              " " << (faceSetPtr[1]->empty()));
9764     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9765   }
9766
9767   // ====================================================================
9768   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9769   // ====================================================================
9770
9771   // delete temporary faces
9772   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9773   //  while ( tmpFaceIt->more() )
9774   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9775   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9776   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9777     aMesh->RemoveElement(*tmpFaceIt);
9778
9779   if ( aResult != SEW_OK)
9780     return aResult;
9781
9782   list< int > nodeIDsToRemove;
9783   vector< const SMDS_MeshNode*> nodes;
9784   ElemFeatures elemType;
9785
9786   // loop on nodes replacement map
9787   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9788   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9789     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9790     {
9791       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9792       nodeIDsToRemove.push_back( nToRemove->GetID() );
9793       // loop on elements sharing nToRemove
9794       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9795       while ( invElemIt->more() ) {
9796         const SMDS_MeshElement* e = invElemIt->next();
9797         // get a new suite of nodes: make replacement
9798         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9799         nodes.resize( nbNodes );
9800         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9801         while ( nIt->more() ) {
9802           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9803           nnIt = nReplaceMap.find( n );
9804           if ( nnIt != nReplaceMap.end() ) {
9805             nbReplaced++;
9806             n = (*nnIt).second;
9807           }
9808           nodes[ i++ ] = n;
9809         }
9810         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9811         //         elemIDsToRemove.push_back( e->GetID() );
9812         //       else
9813         if ( nbReplaced )
9814         {
9815           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9816           aMesh->RemoveElement( e );
9817
9818           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9819           {
9820             AddToSameGroups( newElem, e, aMesh );
9821             if ( int aShapeId = e->getshapeId() )
9822               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9823           }
9824         }
9825       }
9826     }
9827
9828   Remove( nodeIDsToRemove, true );
9829
9830   return aResult;
9831 }
9832
9833 //================================================================================
9834 /*!
9835  * \brief Find corresponding nodes in two sets of faces
9836  * \param theSide1 - first face set
9837  * \param theSide2 - second first face
9838  * \param theFirstNode1 - a boundary node of set 1
9839  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9840  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9841  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9842  * \param nReplaceMap - output map of corresponding nodes
9843  * \return bool  - is a success or not
9844  */
9845 //================================================================================
9846
9847 #ifdef _DEBUG_
9848 //#define DEBUG_MATCHING_NODES
9849 #endif
9850
9851 SMESH_MeshEditor::Sew_Error
9852 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9853                                     set<const SMDS_MeshElement*>& theSide2,
9854                                     const SMDS_MeshNode*          theFirstNode1,
9855                                     const SMDS_MeshNode*          theFirstNode2,
9856                                     const SMDS_MeshNode*          theSecondNode1,
9857                                     const SMDS_MeshNode*          theSecondNode2,
9858                                     TNodeNodeMap &                nReplaceMap)
9859 {
9860   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9861
9862   nReplaceMap.clear();
9863   if ( theFirstNode1 != theFirstNode2 )
9864     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9865   if ( theSecondNode1 != theSecondNode2 )
9866     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9867
9868   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9869   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9870
9871   list< NLink > linkList[2];
9872   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9873   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9874
9875   // loop on links in linkList; find faces by links and append links
9876   // of the found faces to linkList
9877   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9878   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9879     NLink link[] = { *linkIt[0], *linkIt[1] };
9880     if ( linkSet.find( link[0] ) == linkSet.end() )
9881       continue;
9882
9883     // by links, find faces in the face sets,
9884     // and find indices of link nodes in the found faces;
9885     // in a face set, there is only one or no face sharing a link
9886     // ---------------------------------------------------------------
9887
9888     const SMDS_MeshElement* face[] = { 0, 0 };
9889     list<const SMDS_MeshNode*> notLinkNodes[2];
9890     //bool reverse[] = { false, false }; // order of notLinkNodes
9891     int nbNodes[2];
9892     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9893     {
9894       const SMDS_MeshNode* n1 = link[iSide].first;
9895       const SMDS_MeshNode* n2 = link[iSide].second;
9896       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9897       set< const SMDS_MeshElement* > facesOfNode1;
9898       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9899       {
9900         // during a loop of the first node, we find all faces around n1,
9901         // during a loop of the second node, we find one face sharing both n1 and n2
9902         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9903         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9904         while ( fIt->more() ) { // loop on faces sharing a node
9905           const SMDS_MeshElement* f = fIt->next();
9906           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9907               ! facesOfNode1.insert( f ).second ) // f encounters twice
9908           {
9909             if ( face[ iSide ] ) {
9910               MESSAGE( "2 faces per link " );
9911               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9912             }
9913             face[ iSide ] = f;
9914             faceSet->erase( f );
9915
9916             // get not link nodes
9917             int nbN = f->NbNodes();
9918             if ( f->IsQuadratic() )
9919               nbN /= 2;
9920             nbNodes[ iSide ] = nbN;
9921             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9922             int i1 = f->GetNodeIndex( n1 );
9923             int i2 = f->GetNodeIndex( n2 );
9924             int iEnd = nbN, iBeg = -1, iDelta = 1;
9925             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9926             if ( reverse ) {
9927               std::swap( iEnd, iBeg ); iDelta = -1;
9928             }
9929             int i = i2;
9930             while ( true ) {
9931               i += iDelta;
9932               if ( i == iEnd ) i = iBeg + iDelta;
9933               if ( i == i1 ) break;
9934               nodes.push_back ( f->GetNode( i ) );
9935             }
9936           }
9937         }
9938       }
9939     }
9940     // check similarity of elements of the sides
9941     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9942       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9943       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9944         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9945       }
9946       else {
9947         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9948       }
9949     }
9950
9951     // set nodes to merge
9952     // -------------------
9953
9954     if ( face[0] && face[1] )  {
9955       if ( nbNodes[0] != nbNodes[1] ) {
9956         MESSAGE("Diff nb of face nodes");
9957         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9958       }
9959 #ifdef DEBUG_MATCHING_NODES
9960       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9961                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9962                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9963 #endif
9964       int nbN = nbNodes[0];
9965       {
9966         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9967         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9968         for ( int i = 0 ; i < nbN - 2; ++i ) {
9969 #ifdef DEBUG_MATCHING_NODES
9970           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9971 #endif
9972           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9973         }
9974       }
9975
9976       // add other links of the face 1 to linkList
9977       // -----------------------------------------
9978
9979       const SMDS_MeshElement* f0 = face[0];
9980       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9981       for ( int i = 0; i < nbN; i++ )
9982       {
9983         const SMDS_MeshNode* n2 = f0->GetNode( i );
9984         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9985           linkSet.insert( SMESH_TLink( n1, n2 ));
9986         if ( !iter_isnew.second ) { // already in a set: no need to process
9987           linkSet.erase( iter_isnew.first );
9988         }
9989         else // new in set == encountered for the first time: add
9990         {
9991 #ifdef DEBUG_MATCHING_NODES
9992           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9993                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9994 #endif
9995           linkList[0].push_back ( NLink( n1, n2 ));
9996           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9997         }
9998         n1 = n2;
9999       }
10000     } // 2 faces found
10001   } // loop on link lists
10002
10003   return SEW_OK;
10004 }
10005
10006 namespace // automatically find theAffectedElems for DoubleNodes()
10007 {
10008   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10009
10010   //--------------------------------------------------------------------------------
10011   // Nodes shared by adjacent FissureBorder's.
10012   // 1 node  if FissureBorder separates faces
10013   // 2 nodes if FissureBorder separates volumes
10014   struct SubBorder
10015   {
10016     const SMDS_MeshNode* _nodes[2];
10017     int                  _nbNodes;
10018
10019     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10020     {
10021       _nodes[0] = n1;
10022       _nodes[1] = n2;
10023       _nbNodes = bool( n1 ) + bool( n2 );
10024       if ( _nbNodes == 2 && n1 > n2 )
10025         std::swap( _nodes[0], _nodes[1] );
10026     }
10027     bool operator<( const SubBorder& other ) const
10028     {
10029       for ( int i = 0; i < _nbNodes; ++i )
10030       {
10031         if ( _nodes[i] < other._nodes[i] ) return true;
10032         if ( _nodes[i] > other._nodes[i] ) return false;
10033       }
10034       return false;
10035     }
10036   };
10037
10038   //--------------------------------------------------------------------------------
10039   // Map a SubBorder to all FissureBorder it bounds
10040   struct FissureBorder;
10041   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10042   typedef TBorderLinks::iterator                               TMappedSub;
10043
10044   //--------------------------------------------------------------------------------
10045   /*!
10046    * \brief Element border (volume facet or face edge) at a fissure
10047    */
10048   struct FissureBorder
10049   {
10050     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10051     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10052
10053     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10054     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10055
10056     FissureBorder( FissureBorder && from ) // move constructor
10057     {
10058       std::swap( _nodes,       from._nodes );
10059       std::swap( _sortedNodes, from._sortedNodes );
10060       _elems[0] = from._elems[0];
10061       _elems[1] = from._elems[1];
10062     }
10063
10064     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10065                    std::vector< const SMDS_MeshElement* > & adjElems)
10066       : _nodes( elemToDuplicate->NbCornerNodes() )
10067     {
10068       for ( size_t i = 0; i < _nodes.size(); ++i )
10069         _nodes[i] = elemToDuplicate->GetNode( i );
10070
10071       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10072       findAdjacent( type, adjElems );
10073     }
10074
10075     FissureBorder( const SMDS_MeshNode**                    nodes,
10076                    const size_t                             nbNodes,
10077                    const SMDSAbs_ElementType                adjElemsType,
10078                    std::vector< const SMDS_MeshElement* > & adjElems)
10079       : _nodes( nodes, nodes + nbNodes )
10080     {
10081       findAdjacent( adjElemsType, adjElems );
10082     }
10083
10084     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10085                        std::vector< const SMDS_MeshElement* > & adjElems)
10086     {
10087       _elems[0] = _elems[1] = 0;
10088       adjElems.clear();
10089       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10090         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10091           _elems[i] = adjElems[i];
10092     }
10093
10094     bool operator<( const FissureBorder& other ) const
10095     {
10096       return GetSortedNodes() < other.GetSortedNodes();
10097     }
10098
10099     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10100     {
10101       if ( _sortedNodes.empty() && !_nodes.empty() )
10102       {
10103         FissureBorder* me = const_cast<FissureBorder*>( this );
10104         me->_sortedNodes = me->_nodes;
10105         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10106       }
10107       return _sortedNodes;
10108     }
10109
10110     size_t NbSub() const
10111     {
10112       return _nodes.size();
10113     }
10114
10115     SubBorder Sub(size_t i) const
10116     {
10117       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10118     }
10119
10120     void AddSelfTo( TBorderLinks& borderLinks )
10121     {
10122       _mappedSubs.resize( NbSub() );
10123       for ( size_t i = 0; i < NbSub(); ++i )
10124       {
10125         TBorderLinks::iterator s2b =
10126           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10127         s2b->second.push_back( this );
10128         _mappedSubs[ i ] = s2b;
10129       }
10130     }
10131
10132     void Clear()
10133     {
10134       _nodes.clear();
10135     }
10136
10137     const SMDS_MeshElement* GetMarkedElem() const
10138     {
10139       if ( _nodes.empty() ) return 0; // cleared
10140       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10141       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10142       return 0;
10143     }
10144
10145     gp_XYZ GetNorm() const // normal to the border
10146     {
10147       gp_XYZ norm;
10148       if ( _nodes.size() == 2 )
10149       {
10150         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10151         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10152           avgNorm += norm;
10153         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10154           avgNorm += norm;
10155
10156         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10157         norm = bordDir ^ avgNorm;
10158       }
10159       else
10160       {
10161         SMESH_NodeXYZ p0( _nodes[0] );
10162         SMESH_NodeXYZ p1( _nodes[1] );
10163         SMESH_NodeXYZ p2( _nodes[2] );
10164         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10165       }
10166       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10167         norm.Reverse();
10168
10169       return norm;
10170     }
10171
10172     void ChooseSide() // mark an _elem located at positive side of fissure
10173     {
10174       _elems[0]->setIsMarked( true );
10175       gp_XYZ norm = GetNorm();
10176       double maxX = norm.Coord(1);
10177       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10178       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10179       if ( maxX < 0 )
10180       {
10181         _elems[0]->setIsMarked( false );
10182         _elems[1]->setIsMarked( true );
10183       }
10184     }
10185
10186   }; // struct FissureBorder
10187
10188   //--------------------------------------------------------------------------------
10189   /*!
10190    * \brief Classifier of elements at fissure edge
10191    */
10192   class FissureNormal
10193   {
10194     std::vector< gp_XYZ > _normals;
10195     bool                  _bothIn;
10196
10197   public:
10198     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10199     {
10200       _bothIn = false;
10201       _normals.reserve(2);
10202       _normals.push_back( bord.GetNorm() );
10203       if ( _normals.size() == 2 )
10204         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10205     }
10206
10207     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10208     {
10209       bool isIn = false;
10210       switch ( _normals.size() ) {
10211       case 1:
10212       {
10213         isIn = !isOut( n, _normals[0], elem );
10214         break;
10215       }
10216       case 2:
10217       {
10218         bool in1 = !isOut( n, _normals[0], elem );
10219         bool in2 = !isOut( n, _normals[1], elem );
10220         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10221       }
10222       }
10223       return isIn;
10224     }
10225   };
10226
10227   //================================================================================
10228   /*!
10229    * \brief Classify an element by a plane passing through a node
10230    */
10231   //================================================================================
10232
10233   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10234   {
10235     SMESH_NodeXYZ p = n;
10236     double sumDot = 0;
10237     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10238     {
10239       SMESH_NodeXYZ pi = elem->GetNode( i );
10240       sumDot += norm * ( pi - p );
10241     }
10242     return sumDot < -1e-100;
10243   }
10244
10245   //================================================================================
10246   /*!
10247    * \brief Find FissureBorder's by nodes to duplicate
10248    */
10249   //================================================================================
10250
10251   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10252                            std::vector< FissureBorder > & theFissureBorders )
10253   {
10254     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10255     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10256     if ( !n ) return;
10257     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10258     if ( n->NbInverseElements( elemType ) == 0 )
10259     {
10260       elemType = SMDSAbs_Face;
10261       if ( n->NbInverseElements( elemType ) == 0 )
10262         return;
10263     }
10264     // unmark elements touching the fissure
10265     for ( ; nIt != theNodes.end(); ++nIt )
10266       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10267
10268     // loop on elements touching the fissure to get their borders belonging to the fissure
10269     std::set< FissureBorder >              fissureBorders;
10270     std::vector< const SMDS_MeshElement* > adjElems;
10271     std::vector< const SMDS_MeshNode* >    nodes;
10272     SMDS_VolumeTool volTool;
10273     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10274     {
10275       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10276       while ( invIt->more() )
10277       {
10278         const SMDS_MeshElement* eInv = invIt->next();
10279         if ( eInv->isMarked() ) continue;
10280         eInv->setIsMarked( true );
10281
10282         if ( elemType == SMDSAbs_Volume )
10283         {
10284           volTool.Set( eInv );
10285           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10286           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10287           {
10288             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10289             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10290             nodes.clear();
10291             bool allOnFissure = true;
10292             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10293               if (( allOnFissure = theNodes.count( nn[ iN ])))
10294                 nodes.push_back( nn[ iN ]);
10295             if ( allOnFissure )
10296               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10297                                                                elemType, adjElems )));
10298           }
10299         }
10300         else // elemType == SMDSAbs_Face
10301         {
10302           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10303           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10304           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10305           {
10306             nn[1]      = eInv->GetNode( iN );
10307             onFissure1 = theNodes.count( nn[1] );
10308             if ( onFissure0 && onFissure1 )
10309               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10310             nn[0]      = nn[1];
10311             onFissure0 = onFissure1;
10312           }
10313         }
10314       }
10315     }
10316
10317     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10318     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10319     for ( ; bord != fissureBorders.end(); ++bord )
10320     {
10321       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10322     }
10323     return;
10324   } // findFissureBorders()
10325
10326   //================================================================================
10327   /*!
10328    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10329    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10330    *  \param [in] theNodesNot - nodes not to duplicate
10331    *  \param [out] theAffectedElems - the found elements
10332    */
10333   //================================================================================
10334
10335   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10336                           TIDSortedElemSet&       theAffectedElems)
10337   {
10338     if ( theElemsOrNodes.empty() ) return;
10339
10340     // find FissureBorder's
10341
10342     std::vector< FissureBorder >           fissure;
10343     std::vector< const SMDS_MeshElement* > elemsByFacet;
10344
10345     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10346     if ( (*elIt)->GetType() == SMDSAbs_Node )
10347     {
10348       findFissureBorders( theElemsOrNodes, fissure );
10349     }
10350     else
10351     {
10352       fissure.reserve( theElemsOrNodes.size() );
10353       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10354         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10355     }
10356     if ( fissure.empty() )
10357       return;
10358
10359     // fill borderLinks
10360
10361     TBorderLinks borderLinks;
10362
10363     for ( size_t i = 0; i < fissure.size(); ++i )
10364     {
10365       fissure[i].AddSelfTo( borderLinks );
10366     }
10367
10368     // get theAffectedElems
10369
10370     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10371     for ( size_t i = 0; i < fissure.size(); ++i )
10372       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10373       {
10374         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10375                                         false, /*markElem=*/true );
10376       }
10377
10378     std::vector<const SMDS_MeshNode *>                 facetNodes;
10379     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10380     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10381
10382     // choose a side of fissure
10383     fissure[0].ChooseSide();
10384     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10385
10386     size_t nbCheckedBorders = 0;
10387     while ( nbCheckedBorders < fissure.size() )
10388     {
10389       // find a FissureBorder to treat
10390       FissureBorder* bord = 0;
10391       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10392         if ( fissure[i].GetMarkedElem() )
10393           bord = & fissure[i];
10394       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10395         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10396         {
10397           bord = & fissure[i];
10398           bord->ChooseSide();
10399           theAffectedElems.insert( bord->GetMarkedElem() );
10400         }
10401       if ( !bord ) return;
10402       ++nbCheckedBorders;
10403
10404       // treat FissureBorder's linked to bord
10405       fissureNodes.clear();
10406       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10407       for ( size_t i = 0; i < bord->NbSub(); ++i )
10408       {
10409         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10410         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10411         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10412         const SubBorder&                          sb = l2b->first;
10413         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10414
10415         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10416         {
10417           for ( int j = 0; j < sb._nbNodes; ++j )
10418             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10419           continue;
10420         }
10421
10422         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10423         // until an elem adjacent to a neighbour FissureBorder is found
10424         facetNodes.clear();
10425         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10426         facetNodes.resize( sb._nbNodes + 1 );
10427
10428         while ( bordElem )
10429         {
10430           // check if bordElem is adjacent to a neighbour FissureBorder
10431           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10432           {
10433             FissureBorder* bord2 = linkedBorders[j];
10434             if ( bord2 == bord ) continue;
10435             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10436               bordElem = 0;
10437             else
10438               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10439           }
10440           if ( !bordElem )
10441             break;
10442
10443           // find the next bordElem
10444           const SMDS_MeshElement* nextBordElem = 0;
10445           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10446           {
10447             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10448             if ( fissureNodes.count( n )) continue;
10449
10450             facetNodes[ sb._nbNodes ] = n;
10451             elemsByFacet.clear();
10452             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10453             {
10454               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10455                 if ( elemsByFacet[ iE ] != bordElem &&
10456                      !elemsByFacet[ iE ]->isMarked() )
10457                 {
10458                   theAffectedElems.insert( elemsByFacet[ iE ]);
10459                   elemsByFacet[ iE ]->setIsMarked( true );
10460                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10461                     nextBordElem = elemsByFacet[ iE ];
10462                 }
10463             }
10464           }
10465           bordElem = nextBordElem;
10466
10467         } // while ( bordElem )
10468
10469         linkedBorders.clear(); // not to treat this link any more
10470
10471       } // loop on SubBorder's of a FissureBorder
10472
10473       bord->Clear();
10474
10475     } // loop on FissureBorder's
10476
10477
10478     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10479
10480     // mark nodes of theAffectedElems
10481     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10482
10483     // unmark nodes of the fissure
10484     elIt = theElemsOrNodes.begin();
10485     if ( (*elIt)->GetType() == SMDSAbs_Node )
10486       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10487     else
10488       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10489
10490     std::vector< gp_XYZ > normVec;
10491
10492     // loop on nodes of the fissure, add elements having marked nodes
10493     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10494     {
10495       const SMDS_MeshElement* e = (*elIt);
10496       if ( e->GetType() != SMDSAbs_Node )
10497         e->setIsMarked( true ); // avoid adding a fissure element
10498
10499       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10500       {
10501         const SMDS_MeshNode* n = e->GetNode( iN );
10502         if ( fissEdgeNodes2Norm.count( n ))
10503           continue;
10504
10505         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10506         while ( invIt->more() )
10507         {
10508           const SMDS_MeshElement* eInv = invIt->next();
10509           if ( eInv->isMarked() ) continue;
10510           eInv->setIsMarked( true );
10511
10512           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10513           while( nIt->more() )
10514             if ( nIt->next()->isMarked())
10515             {
10516               theAffectedElems.insert( eInv );
10517               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10518               n->setIsMarked( false );
10519               break;
10520             }
10521         }
10522       }
10523     }
10524
10525     // add elements on the fissure edge
10526     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10527     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10528     {
10529       const SMDS_MeshNode* edgeNode = n2N->first;
10530       const FissureNormal & normals = n2N->second;
10531
10532       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10533       while ( invIt->more() )
10534       {
10535         const SMDS_MeshElement* eInv = invIt->next();
10536         if ( eInv->isMarked() ) continue;
10537         eInv->setIsMarked( true );
10538
10539         // classify eInv using normals
10540         bool toAdd = normals.IsIn( edgeNode, eInv );
10541         if ( toAdd ) // check if all nodes lie on the fissure edge
10542         {
10543           bool notOnEdge = false;
10544           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10545             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10546           toAdd = notOnEdge;
10547         }
10548         if ( toAdd )
10549         {
10550           theAffectedElems.insert( eInv );
10551         }
10552       }
10553     }
10554
10555     return;
10556   } // findAffectedElems()
10557 } // namespace
10558
10559 //================================================================================
10560 /*!
10561  * \brief Create elements equal (on same nodes) to given ones
10562  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10563  *              elements of the uppest dimension are duplicated.
10564  */
10565 //================================================================================
10566
10567 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10568 {
10569   ClearLastCreated();
10570   SMESHDS_Mesh* mesh = GetMeshDS();
10571
10572   // get an element type and an iterator over elements
10573
10574   SMDSAbs_ElementType type = SMDSAbs_All;
10575   SMDS_ElemIteratorPtr elemIt;
10576   if ( theElements.empty() )
10577   {
10578     if ( mesh->NbNodes() == 0 )
10579       return;
10580     // get most complex type
10581     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10582       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10583       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10584     };
10585     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10586       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10587       {
10588         type = types[i];
10589         elemIt = mesh->elementsIterator( type );
10590         break;
10591       }
10592   }
10593   else
10594   {
10595     //type = (*theElements.begin())->GetType();
10596     elemIt = SMESHUtils::elemSetIterator( theElements );
10597   }
10598
10599   // un-mark all elements to avoid duplicating just created elements
10600   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10601
10602   // duplicate elements
10603
10604   ElemFeatures elemType;
10605
10606   vector< const SMDS_MeshNode* > nodes;
10607   while ( elemIt->more() )
10608   {
10609     const SMDS_MeshElement* elem = elemIt->next();
10610     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10611         ( elem->isMarked() ))
10612       continue;
10613
10614     elemType.Init( elem, /*basicOnly=*/false );
10615     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10616
10617     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10618       newElem->setIsMarked( true );
10619   }
10620 }
10621
10622 //================================================================================
10623 /*!
10624   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10625   \param theElems - the list of elements (edges or faces) to be replicated
10626   The nodes for duplication could be found from these elements
10627   \param theNodesNot - list of nodes to NOT replicate
10628   \param theAffectedElems - the list of elements (cells and edges) to which the
10629   replicated nodes should be associated to.
10630   \return TRUE if operation has been completed successfully, FALSE otherwise
10631 */
10632 //================================================================================
10633
10634 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10635                                     const TIDSortedElemSet& theNodesNot,
10636                                     const TIDSortedElemSet& theAffectedElems )
10637 {
10638   ClearLastCreated();
10639
10640   if ( theElems.size() == 0 )
10641     return false;
10642
10643   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10644   if ( !aMeshDS )
10645     return false;
10646
10647   bool res = false;
10648   TNodeNodeMap anOldNodeToNewNode;
10649   // duplicate elements and nodes
10650   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10651   // replce nodes by duplications
10652   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10653   return res;
10654 }
10655
10656 //================================================================================
10657 /*!
10658   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10659   \param theMeshDS - mesh instance
10660   \param theElems - the elements replicated or modified (nodes should be changed)
10661   \param theNodesNot - nodes to NOT replicate
10662   \param theNodeNodeMap - relation of old node to new created node
10663   \param theIsDoubleElem - flag os to replicate element or modify
10664   \return TRUE if operation has been completed successfully, FALSE otherwise
10665 */
10666 //================================================================================
10667
10668 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10669                                    const TIDSortedElemSet& theElems,
10670                                    const TIDSortedElemSet& theNodesNot,
10671                                    TNodeNodeMap&           theNodeNodeMap,
10672                                    const bool              theIsDoubleElem )
10673 {
10674   // iterate through element and duplicate them (by nodes duplication)
10675   bool res = false;
10676   std::vector<const SMDS_MeshNode*> newNodes;
10677   ElemFeatures elemType;
10678
10679   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10680   for ( ;  elemItr != theElems.end(); ++elemItr )
10681   {
10682     const SMDS_MeshElement* anElem = *elemItr;
10683     // if (!anElem)
10684     //   continue;
10685
10686     // duplicate nodes to duplicate element
10687     bool isDuplicate = false;
10688     newNodes.resize( anElem->NbNodes() );
10689     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10690     int ind = 0;
10691     while ( anIter->more() )
10692     {
10693       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10694       const SMDS_MeshNode*  aNewNode = aCurrNode;
10695       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10696       if ( n2n != theNodeNodeMap.end() )
10697       {
10698         aNewNode = n2n->second;
10699       }
10700       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10701       {
10702         // duplicate node
10703         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10704         copyPosition( aCurrNode, aNewNode );
10705         theNodeNodeMap[ aCurrNode ] = aNewNode;
10706         myLastCreatedNodes.push_back( aNewNode );
10707       }
10708       isDuplicate |= (aCurrNode != aNewNode);
10709       newNodes[ ind++ ] = aNewNode;
10710     }
10711     if ( !isDuplicate )
10712       continue;
10713
10714     if ( theIsDoubleElem )
10715       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10716     else
10717       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10718
10719     res = true;
10720   }
10721   return res;
10722 }
10723
10724 //================================================================================
10725 /*!
10726   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10727   \param theNodes - identifiers of nodes to be doubled
10728   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10729   nodes. If list of element identifiers is empty then nodes are doubled but
10730   they not assigned to elements
10731   \return TRUE if operation has been completed successfully, FALSE otherwise
10732 */
10733 //================================================================================
10734
10735 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10736                                     const std::list< int >& theListOfModifiedElems )
10737 {
10738   ClearLastCreated();
10739
10740   if ( theListOfNodes.size() == 0 )
10741     return false;
10742
10743   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10744   if ( !aMeshDS )
10745     return false;
10746
10747   // iterate through nodes and duplicate them
10748
10749   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10750
10751   std::list< int >::const_iterator aNodeIter;
10752   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10753   {
10754     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10755     if ( !aNode )
10756       continue;
10757
10758     // duplicate node
10759
10760     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10761     if ( aNewNode )
10762     {
10763       copyPosition( aNode, aNewNode );
10764       anOldNodeToNewNode[ aNode ] = aNewNode;
10765       myLastCreatedNodes.push_back( aNewNode );
10766     }
10767   }
10768
10769   // Change nodes of elements
10770
10771   std::vector<const SMDS_MeshNode*> aNodeArr;
10772
10773   std::list< int >::const_iterator anElemIter;
10774   for ( anElemIter =  theListOfModifiedElems.begin();
10775         anElemIter != theListOfModifiedElems.end();
10776         anElemIter++ )
10777   {
10778     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10779     if ( !anElem )
10780       continue;
10781
10782     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10783     for( size_t i = 0; i < aNodeArr.size(); ++i )
10784     {
10785       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10786         anOldNodeToNewNode.find( aNodeArr[ i ]);
10787       if ( n2n != anOldNodeToNewNode.end() )
10788         aNodeArr[ i ] = n2n->second;
10789     }
10790     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10791   }
10792
10793   return true;
10794 }
10795
10796 namespace {
10797
10798   //================================================================================
10799   /*!
10800     \brief Check if element located inside shape
10801     \return TRUE if IN or ON shape, FALSE otherwise
10802   */
10803   //================================================================================
10804
10805   template<class Classifier>
10806   bool isInside(const SMDS_MeshElement* theElem,
10807                 Classifier&             theClassifier,
10808                 const double            theTol)
10809   {
10810     gp_XYZ centerXYZ (0, 0, 0);
10811     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10812       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10813
10814     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10815     theClassifier.Perform(aPnt, theTol);
10816     TopAbs_State aState = theClassifier.State();
10817     return (aState == TopAbs_IN || aState == TopAbs_ON );
10818   }
10819
10820   //================================================================================
10821   /*!
10822    * \brief Classifier of the 3D point on the TopoDS_Face
10823    *        with interaface suitable for isInside()
10824    */
10825   //================================================================================
10826
10827   struct _FaceClassifier
10828   {
10829     Extrema_ExtPS       _extremum;
10830     BRepAdaptor_Surface _surface;
10831     TopAbs_State        _state;
10832
10833     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10834     {
10835       _extremum.Initialize( _surface,
10836                             _surface.FirstUParameter(), _surface.LastUParameter(),
10837                             _surface.FirstVParameter(), _surface.LastVParameter(),
10838                             _surface.Tolerance(), _surface.Tolerance() );
10839     }
10840     void Perform(const gp_Pnt& aPnt, double theTol)
10841     {
10842       theTol *= theTol;
10843       _state = TopAbs_OUT;
10844       _extremum.Perform(aPnt);
10845       if ( _extremum.IsDone() )
10846         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10847           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10848     }
10849     TopAbs_State State() const
10850     {
10851       return _state;
10852     }
10853   };
10854 }
10855
10856 //================================================================================
10857 /*!
10858   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10859   This method is the first step of DoubleNodeElemGroupsInRegion.
10860   \param theElems - list of groups of elements (edges or faces) to be replicated
10861   \param theNodesNot - list of groups of nodes not to replicated
10862   \param theShape - shape to detect affected elements (element which geometric center
10863          located on or inside shape). If the shape is null, detection is done on faces orientations
10864          (select elements with a gravity center on the side given by faces normals).
10865          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10866          The replicated nodes should be associated to affected elements.
10867   \return true
10868   \sa DoubleNodeElemGroupsInRegion()
10869 */
10870 //================================================================================
10871
10872 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10873                                                    const TIDSortedElemSet& theNodesNot,
10874                                                    const TopoDS_Shape&     theShape,
10875                                                    TIDSortedElemSet&       theAffectedElems)
10876 {
10877   if ( theShape.IsNull() )
10878   {
10879     findAffectedElems( theElems, theAffectedElems );
10880   }
10881   else
10882   {
10883     const double aTol = Precision::Confusion();
10884     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10885     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10886     if ( theShape.ShapeType() == TopAbs_SOLID )
10887     {
10888       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10889       bsc3d->PerformInfinitePoint(aTol);
10890     }
10891     else if (theShape.ShapeType() == TopAbs_FACE )
10892     {
10893       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10894     }
10895
10896     // iterates on indicated elements and get elements by back references from their nodes
10897     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10898     for ( ;  elemItr != theElems.end(); ++elemItr )
10899     {
10900       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10901       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10902       while ( nodeItr->more() )
10903       {
10904         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10905         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10906           continue;
10907         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10908         while ( backElemItr->more() )
10909         {
10910           const SMDS_MeshElement* curElem = backElemItr->next();
10911           if ( curElem && theElems.find(curElem) == theElems.end() &&
10912                ( bsc3d.get() ?
10913                  isInside( curElem, *bsc3d, aTol ) :
10914                  isInside( curElem, *aFaceClassifier, aTol )))
10915             theAffectedElems.insert( curElem );
10916         }
10917       }
10918     }
10919   }
10920   return true;
10921 }
10922
10923 //================================================================================
10924 /*!
10925   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10926   \param theElems - group of of elements (edges or faces) to be replicated
10927   \param theNodesNot - group of nodes not to replicate
10928   \param theShape - shape to detect affected elements (element which geometric center
10929   located on or inside shape).
10930   The replicated nodes should be associated to affected elements.
10931   \return TRUE if operation has been completed successfully, FALSE otherwise
10932 */
10933 //================================================================================
10934
10935 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10936                                             const TIDSortedElemSet& theNodesNot,
10937                                             const TopoDS_Shape&     theShape )
10938 {
10939   if ( theShape.IsNull() )
10940     return false;
10941
10942   const double aTol = Precision::Confusion();
10943   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10944   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10945   if ( theShape.ShapeType() == TopAbs_SOLID )
10946   {
10947     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10948     bsc3d->PerformInfinitePoint(aTol);
10949   }
10950   else if (theShape.ShapeType() == TopAbs_FACE )
10951   {
10952     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10953   }
10954
10955   // iterates on indicated elements and get elements by back references from their nodes
10956   TIDSortedElemSet anAffected;
10957   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958   for ( ;  elemItr != theElems.end(); ++elemItr )
10959   {
10960     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10961     if (!anElem)
10962       continue;
10963
10964     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10965     while ( nodeItr->more() )
10966     {
10967       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10968       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10969         continue;
10970       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971       while ( backElemItr->more() )
10972       {
10973         const SMDS_MeshElement* curElem = backElemItr->next();
10974         if ( curElem && theElems.find(curElem) == theElems.end() &&
10975              ( bsc3d ?
10976                isInside( curElem, *bsc3d, aTol ) :
10977                isInside( curElem, *aFaceClassifier, aTol )))
10978           anAffected.insert( curElem );
10979       }
10980     }
10981   }
10982   return DoubleNodes( theElems, theNodesNot, anAffected );
10983 }
10984
10985 /*!
10986  *  \brief compute an oriented angle between two planes defined by four points.
10987  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10988  *  @param p0 base of the rotation axe
10989  *  @param p1 extremity of the rotation axe
10990  *  @param g1 belongs to the first plane
10991  *  @param g2 belongs to the second plane
10992  */
10993 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10994 {
10995   gp_Vec vref(p0, p1);
10996   gp_Vec v1(p0, g1);
10997   gp_Vec v2(p0, g2);
10998   gp_Vec n1 = vref.Crossed(v1);
10999   gp_Vec n2 = vref.Crossed(v2);
11000   try {
11001     return n2.AngleWithRef(n1, vref);
11002   }
11003   catch ( Standard_Failure ) {
11004   }
11005   return Max( v1.Magnitude(), v2.Magnitude() );
11006 }
11007
11008 /*!
11009  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11010  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11011  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11012  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11013  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11014  * 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.
11015  * 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.
11016  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11017  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11018  * \param theElems - list of groups of volumes, where a group of volume is a set of
11019  *        SMDS_MeshElements sorted by Id.
11020  * \param createJointElems - if TRUE, create the elements
11021  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11022  *        the boundary between \a theDomains and the rest mesh
11023  * \return TRUE if operation has been completed successfully, FALSE otherwise
11024  */
11025 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11026                                                      bool                                 createJointElems,
11027                                                      bool                                 onAllBoundaries)
11028 {
11029   // MESSAGE("----------------------------------------------");
11030   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11031   // MESSAGE("----------------------------------------------");
11032
11033   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11034   meshDS->BuildDownWardConnectivity(true);
11035   CHRONO(50);
11036   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11037
11038   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11039   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11040   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11041
11042   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11043   std::map<int,int>celldom; // cell vtkId --> domain
11044   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11045   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11046   faceDomains.clear();
11047   celldom.clear();
11048   cellDomains.clear();
11049   nodeDomains.clear();
11050   std::map<int,int> emptyMap;
11051   std::set<int> emptySet;
11052   emptyMap.clear();
11053
11054   //MESSAGE(".. Number of domains :"<<theElems.size());
11055
11056   TIDSortedElemSet theRestDomElems;
11057   const int iRestDom  = -1;
11058   const int idom0     = onAllBoundaries ? iRestDom : 0;
11059   const int nbDomains = theElems.size();
11060
11061   // Check if the domains do not share an element
11062   for (int idom = 0; idom < nbDomains-1; idom++)
11063   {
11064     //       MESSAGE("... Check of domain #" << idom);
11065     const TIDSortedElemSet& domain = theElems[idom];
11066     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11067     for (; elemItr != domain.end(); ++elemItr)
11068     {
11069       const SMDS_MeshElement* anElem = *elemItr;
11070       int idombisdeb = idom + 1 ;
11071       // check if the element belongs to a domain further in the list
11072       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11073       {
11074         const TIDSortedElemSet& domainbis = theElems[idombis];
11075         if ( domainbis.count( anElem ))
11076         {
11077           MESSAGE(".... Domain #" << idom);
11078           MESSAGE(".... Domain #" << idombis);
11079           throw SALOME_Exception("The domains are not disjoint.");
11080           return false ;
11081         }
11082       }
11083     }
11084   }
11085
11086   for (int idom = 0; idom < nbDomains; idom++)
11087   {
11088
11089     // --- build a map (face to duplicate --> volume to modify)
11090     //     with all the faces shared by 2 domains (group of elements)
11091     //     and corresponding volume of this domain, for each shared face.
11092     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11093
11094     //MESSAGE("... Neighbors of domain #" << idom);
11095     const TIDSortedElemSet& domain = theElems[idom];
11096     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11097     for (; elemItr != domain.end(); ++elemItr)
11098     {
11099       const SMDS_MeshElement* anElem = *elemItr;
11100       if (!anElem)
11101         continue;
11102       int vtkId = anElem->GetVtkID();
11103       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11104       int neighborsVtkIds[NBMAXNEIGHBORS];
11105       int downIds[NBMAXNEIGHBORS];
11106       unsigned char downTypes[NBMAXNEIGHBORS];
11107       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11108       for (int n = 0; n < nbNeighbors; n++)
11109       {
11110         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11111         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11112         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11113         {
11114           bool ok = false;
11115           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11116           {
11117             // MESSAGE("Domain " << idombis);
11118             const TIDSortedElemSet& domainbis = theElems[idombis];
11119             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11120           }
11121           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11122           {
11123             DownIdType face(downIds[n], downTypes[n]);
11124             if (!faceDomains[face].count(idom))
11125             {
11126               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11127               celldom[vtkId] = idom;
11128               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11129             }
11130             if ( !ok )
11131             {
11132               theRestDomElems.insert( elem );
11133               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11134               celldom[neighborsVtkIds[n]] = iRestDom;
11135             }
11136           }
11137         }
11138       }
11139     }
11140   }
11141
11142   //MESSAGE("Number of shared faces " << faceDomains.size());
11143   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11144
11145   // --- explore the shared faces domain by domain,
11146   //     explore the nodes of the face and see if they belong to a cell in the domain,
11147   //     which has only a node or an edge on the border (not a shared face)
11148
11149   for (int idomain = idom0; idomain < nbDomains; idomain++)
11150   {
11151     //MESSAGE("Domain " << idomain);
11152     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11153     itface = faceDomains.begin();
11154     for (; itface != faceDomains.end(); ++itface)
11155     {
11156       const std::map<int, int>& domvol = itface->second;
11157       if (!domvol.count(idomain))
11158         continue;
11159       DownIdType face = itface->first;
11160       //MESSAGE(" --- face " << face.cellId);
11161       std::set<int> oldNodes;
11162       oldNodes.clear();
11163       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11164       std::set<int>::iterator itn = oldNodes.begin();
11165       for (; itn != oldNodes.end(); ++itn)
11166       {
11167         int oldId = *itn;
11168         //MESSAGE("     node " << oldId);
11169         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11170         for (int i=0; i<l.ncells; i++)
11171         {
11172           int vtkId = l.cells[i];
11173           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11174           if (!domain.count(anElem))
11175             continue;
11176           int vtkType = grid->GetCellType(vtkId);
11177           int downId = grid->CellIdToDownId(vtkId);
11178           if (downId < 0)
11179           {
11180             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11181             continue; // not OK at this stage of the algorithm:
11182             //no cells created after BuildDownWardConnectivity
11183           }
11184           DownIdType aCell(downId, vtkType);
11185           cellDomains[aCell][idomain] = vtkId;
11186           celldom[vtkId] = idomain;
11187           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11188         }
11189       }
11190     }
11191   }
11192
11193   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11194   //     for each shared face, get the nodes
11195   //     for each node, for each domain of the face, create a clone of the node
11196
11197   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11198   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11199   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11200
11201   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11202   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11203   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11204
11205   //MESSAGE(".. Duplication of the nodes");
11206   for (int idomain = idom0; idomain < nbDomains; idomain++)
11207   {
11208     itface = faceDomains.begin();
11209     for (; itface != faceDomains.end(); ++itface)
11210     {
11211       const std::map<int, int>& domvol = itface->second;
11212       if (!domvol.count(idomain))
11213         continue;
11214       DownIdType face = itface->first;
11215       //MESSAGE(" --- face " << face.cellId);
11216       std::set<int> oldNodes;
11217       oldNodes.clear();
11218       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11219       std::set<int>::iterator itn = oldNodes.begin();
11220       for (; itn != oldNodes.end(); ++itn)
11221       {
11222         int oldId = *itn;
11223         if (nodeDomains[oldId].empty())
11224         {
11225           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11226           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11227         }
11228         std::map<int, int>::const_iterator itdom = domvol.begin();
11229         for (; itdom != domvol.end(); ++itdom)
11230         {
11231           int idom = itdom->first;
11232           //MESSAGE("         domain " << idom);
11233           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11234           {
11235             if (nodeDomains[oldId].size() >= 2) // a multiple node
11236             {
11237               vector<int> orderedDoms;
11238               //MESSAGE("multiple node " << oldId);
11239               if (mutipleNodes.count(oldId))
11240                 orderedDoms = mutipleNodes[oldId];
11241               else
11242               {
11243                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11244                 for (; it != nodeDomains[oldId].end(); ++it)
11245                   orderedDoms.push_back(it->first);
11246               }
11247               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11248               //stringstream txt;
11249               //for (int i=0; i<orderedDoms.size(); i++)
11250               //  txt << orderedDoms[i] << " ";
11251               //MESSAGE("orderedDoms " << txt.str());
11252               mutipleNodes[oldId] = orderedDoms;
11253             }
11254             double *coords = grid->GetPoint(oldId);
11255             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11256             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11257             int newId = newNode->GetVtkID();
11258             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11259             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11260           }
11261         }
11262       }
11263     }
11264   }
11265
11266   //MESSAGE(".. Creation of elements");
11267   for (int idomain = idom0; idomain < nbDomains; idomain++)
11268   {
11269     itface = faceDomains.begin();
11270     for (; itface != faceDomains.end(); ++itface)
11271     {
11272       std::map<int, int> domvol = itface->second;
11273       if (!domvol.count(idomain))
11274         continue;
11275       DownIdType face = itface->first;
11276       //MESSAGE(" --- face " << face.cellId);
11277       std::set<int> oldNodes;
11278       oldNodes.clear();
11279       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11280       int nbMultipleNodes = 0;
11281       std::set<int>::iterator itn = oldNodes.begin();
11282       for (; itn != oldNodes.end(); ++itn)
11283       {
11284         int oldId = *itn;
11285         if (mutipleNodes.count(oldId))
11286           nbMultipleNodes++;
11287       }
11288       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11289       {
11290         //MESSAGE("multiple Nodes detected on a shared face");
11291         int downId = itface->first.cellId;
11292         unsigned char cellType = itface->first.cellType;
11293         // --- shared edge or shared face ?
11294         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11295         {
11296           int nodes[3];
11297           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11298           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11299             if (mutipleNodes.count(nodes[i]))
11300               if (!mutipleNodesToFace.count(nodes[i]))
11301                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11302         }
11303         else // shared face (between two volumes)
11304         {
11305           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11306           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11307           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11308           for (int ie =0; ie < nbEdges; ie++)
11309           {
11310             int nodes[3];
11311             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11312             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11313             {
11314               vector<int> vn0 = mutipleNodes[nodes[0]];
11315               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11316               vector<int> doms;
11317               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11318                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11319                   if ( vn0[i0] == vn1[i1] )
11320                     doms.push_back( vn0[ i0 ]);
11321               if ( doms.size() > 2 )
11322               {
11323                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11324                 double *coords = grid->GetPoint(nodes[0]);
11325                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11326                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11327                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11328                 gp_Pnt gref;
11329                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11330                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11331                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11332                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11333                 for ( size_t id = 0; id < doms.size(); id++ )
11334                 {
11335                   int idom = doms[id];
11336                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11337                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11338                   {
11339                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11340                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11341                     if (domain.count(elem))
11342                     {
11343                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11344                       domvol[idom] = (SMDS_MeshVolume*) svol;
11345                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11346                       double values[3] = { 0,0,0 };
11347                       vtkIdType npts = 0;
11348                       vtkIdType* pts = 0;
11349                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11350                       for ( vtkIdType i = 0; i < npts; ++i )
11351                       {
11352                         double *coords = grid->GetPoint( pts[i] );
11353                         for ( int j = 0; j < 3; ++j )
11354                           values[j] += coords[j] / npts;
11355                       }
11356                       if ( id == 0 )
11357                       {
11358                         gref.SetCoord( values[0], values[1], values[2] );
11359                         angleDom[idom] = 0;
11360                       }
11361                       else
11362                       {
11363                         gp_Pnt g( values[0], values[1], values[2] );
11364                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11365                         //MESSAGE("  angle=" << angleDom[idom]);
11366                       }
11367                       break;
11368                     }
11369                   }
11370                 }
11371                 map<double, int> sortedDom; // sort domains by angle
11372                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11373                   sortedDom[ia->second] = ia->first;
11374                 vector<int> vnodes;
11375                 vector<int> vdom;
11376                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11377                 {
11378                   vdom.push_back(ib->second);
11379                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11380                 }
11381                 for (int ino = 0; ino < nbNodes; ino++)
11382                   vnodes.push_back(nodes[ino]);
11383                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11384               }
11385             }
11386           }
11387         }
11388       }
11389     }
11390   }
11391
11392   // --- iterate on shared faces (volumes to modify, face to extrude)
11393   //     get node id's of the face (id SMDS = id VTK)
11394   //     create flat element with old and new nodes if requested
11395
11396   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11397   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11398
11399   std::map<int, std::map<long,int> > nodeQuadDomains;
11400   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11401
11402   //MESSAGE(".. Creation of elements: simple junction");
11403   if (createJointElems)
11404   {
11405     string joints2DName = "joints2D";
11406     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11407     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11408     string joints3DName = "joints3D";
11409     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11410     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11411
11412     itface = faceDomains.begin();
11413     for (; itface != faceDomains.end(); ++itface)
11414     {
11415       DownIdType face = itface->first;
11416       std::set<int> oldNodes;
11417       std::set<int>::iterator itn;
11418       oldNodes.clear();
11419       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11420
11421       std::map<int, int> domvol = itface->second;
11422       std::map<int, int>::iterator itdom = domvol.begin();
11423       int dom1 = itdom->first;
11424       int vtkVolId = itdom->second;
11425       itdom++;
11426       int dom2 = itdom->first;
11427       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11428                                                        nodeQuadDomains);
11429       stringstream grpname;
11430       grpname << "j_";
11431       if (dom1 < dom2)
11432         grpname << dom1 << "_" << dom2;
11433       else
11434         grpname << dom2 << "_" << dom1;
11435       string namegrp = grpname.str();
11436       if (!mapOfJunctionGroups.count(namegrp))
11437         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11438       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11439       if (sgrp)
11440         sgrp->Add(vol->GetID());
11441       if (vol->GetType() == SMDSAbs_Volume)
11442         joints3DGrp->Add(vol->GetID());
11443       else if (vol->GetType() == SMDSAbs_Face)
11444         joints2DGrp->Add(vol->GetID());
11445     }
11446   }
11447
11448   // --- create volumes on multiple domain intersection if requested
11449   //     iterate on mutipleNodesToFace
11450   //     iterate on edgesMultiDomains
11451
11452   //MESSAGE(".. Creation of elements: multiple junction");
11453   if (createJointElems)
11454   {
11455     // --- iterate on mutipleNodesToFace
11456
11457     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11458     for (; itn != mutipleNodesToFace.end(); ++itn)
11459     {
11460       int node = itn->first;
11461       vector<int> orderDom = itn->second;
11462       vector<vtkIdType> orderedNodes;
11463       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11464         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11465       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11466
11467       stringstream grpname;
11468       grpname << "m2j_";
11469       grpname << 0 << "_" << 0;
11470       string namegrp = grpname.str();
11471       if (!mapOfJunctionGroups.count(namegrp))
11472         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11473       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11474       if (sgrp)
11475         sgrp->Add(face->GetID());
11476     }
11477
11478     // --- iterate on edgesMultiDomains
11479
11480     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11481     for (; ite != edgesMultiDomains.end(); ++ite)
11482     {
11483       vector<int> nodes = ite->first;
11484       vector<int> orderDom = ite->second;
11485       vector<vtkIdType> orderedNodes;
11486       if (nodes.size() == 2)
11487       {
11488         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11489         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11490           if ( orderDom.size() == 3 )
11491             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11492               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11493           else
11494             for (int idom = orderDom.size()-1; idom >=0; idom--)
11495               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11496         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11497
11498         string namegrp = "jointsMultiples";
11499         if (!mapOfJunctionGroups.count(namegrp))
11500           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11501         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11502         if (sgrp)
11503           sgrp->Add(vol->GetID());
11504       }
11505       else
11506       {
11507         //INFOS("Quadratic multiple joints not implemented");
11508         // TODO quadratic nodes
11509       }
11510     }
11511   }
11512
11513   // --- list the explicit faces and edges of the mesh that need to be modified,
11514   //     i.e. faces and edges built with one or more duplicated nodes.
11515   //     associate these faces or edges to their corresponding domain.
11516   //     only the first domain found is kept when a face or edge is shared
11517
11518   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11519   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11520   faceOrEdgeDom.clear();
11521   feDom.clear();
11522
11523   //MESSAGE(".. Modification of elements");
11524   for (int idomain = idom0; idomain < nbDomains; idomain++)
11525   {
11526     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11527     for (; itnod != nodeDomains.end(); ++itnod)
11528     {
11529       int oldId = itnod->first;
11530       //MESSAGE("     node " << oldId);
11531       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11532       for (int i = 0; i < l.ncells; i++)
11533       {
11534         int vtkId = l.cells[i];
11535         int vtkType = grid->GetCellType(vtkId);
11536         int downId = grid->CellIdToDownId(vtkId);
11537         if (downId < 0)
11538           continue; // new cells: not to be modified
11539         DownIdType aCell(downId, vtkType);
11540         int volParents[1000];
11541         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11542         for (int j = 0; j < nbvol; j++)
11543           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11544             if (!feDom.count(vtkId))
11545             {
11546               feDom[vtkId] = idomain;
11547               faceOrEdgeDom[aCell] = emptyMap;
11548               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11549               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11550               //        << " type " << vtkType << " downId " << downId);
11551             }
11552       }
11553     }
11554   }
11555
11556   // --- iterate on shared faces (volumes to modify, face to extrude)
11557   //     get node id's of the face
11558   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11559
11560   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11561   for (int m=0; m<3; m++)
11562   {
11563     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11564     itface = (*amap).begin();
11565     for (; itface != (*amap).end(); ++itface)
11566     {
11567       DownIdType face = itface->first;
11568       std::set<int> oldNodes;
11569       std::set<int>::iterator itn;
11570       oldNodes.clear();
11571       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11572       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11573       std::map<int, int> localClonedNodeIds;
11574
11575       std::map<int, int> domvol = itface->second;
11576       std::map<int, int>::iterator itdom = domvol.begin();
11577       for (; itdom != domvol.end(); ++itdom)
11578       {
11579         int idom = itdom->first;
11580         int vtkVolId = itdom->second;
11581         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11582         localClonedNodeIds.clear();
11583         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11584         {
11585           int oldId = *itn;
11586           if (nodeDomains[oldId].count(idom))
11587           {
11588             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11589             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11590           }
11591         }
11592         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11593       }
11594     }
11595   }
11596
11597   // Remove empty groups (issue 0022812)
11598   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11599   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11600   {
11601     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11602       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11603   }
11604
11605   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11606   grid->DeleteLinks();
11607
11608   CHRONOSTOP(50);
11609   counters::stats();
11610   return true;
11611 }
11612
11613 /*!
11614  * \brief Double nodes on some external faces and create flat elements.
11615  * Flat elements are mainly used by some types of mechanic calculations.
11616  *
11617  * Each group of the list must be constituted of faces.
11618  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11619  * @param theElems - list of groups of faces, where a group of faces is a set of
11620  * SMDS_MeshElements sorted by Id.
11621  * @return TRUE if operation has been completed successfully, FALSE otherwise
11622  */
11623 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11624 {
11625   // MESSAGE("-------------------------------------------------");
11626   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11627   // MESSAGE("-------------------------------------------------");
11628
11629   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11630
11631   // --- For each group of faces
11632   //     duplicate the nodes, create a flat element based on the face
11633   //     replace the nodes of the faces by their clones
11634
11635   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11636   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11637   clonedNodes.clear();
11638   intermediateNodes.clear();
11639   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11640   mapOfJunctionGroups.clear();
11641
11642   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11643   {
11644     const TIDSortedElemSet&           domain = theElems[idom];
11645     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11646     for ( ; elemItr != domain.end(); ++elemItr )
11647     {
11648       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11649       if (!aFace)
11650         continue;
11651       // MESSAGE("aFace=" << aFace->GetID());
11652       bool isQuad = aFace->IsQuadratic();
11653       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11654
11655       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11656
11657       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11658       while (nodeIt->more())
11659       {
11660         const SMDS_MeshNode* node = nodeIt->next();
11661         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11662         if (isMedium)
11663           ln2.push_back(node);
11664         else
11665           ln0.push_back(node);
11666
11667         const SMDS_MeshNode* clone = 0;
11668         if (!clonedNodes.count(node))
11669         {
11670           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11671           copyPosition( node, clone );
11672           clonedNodes[node] = clone;
11673         }
11674         else
11675           clone = clonedNodes[node];
11676
11677         if (isMedium)
11678           ln3.push_back(clone);
11679         else
11680           ln1.push_back(clone);
11681
11682         const SMDS_MeshNode* inter = 0;
11683         if (isQuad && (!isMedium))
11684         {
11685           if (!intermediateNodes.count(node))
11686           {
11687             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11688             copyPosition( node, inter );
11689             intermediateNodes[node] = inter;
11690           }
11691           else
11692             inter = intermediateNodes[node];
11693           ln4.push_back(inter);
11694         }
11695       }
11696
11697       // --- extrude the face
11698
11699       vector<const SMDS_MeshNode*> ln;
11700       SMDS_MeshVolume* vol = 0;
11701       vtkIdType aType = aFace->GetVtkType();
11702       switch (aType)
11703       {
11704       case VTK_TRIANGLE:
11705         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11706         // MESSAGE("vol prism " << vol->GetID());
11707         ln.push_back(ln1[0]);
11708         ln.push_back(ln1[1]);
11709         ln.push_back(ln1[2]);
11710         break;
11711       case VTK_QUAD:
11712         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11713         // MESSAGE("vol hexa " << vol->GetID());
11714         ln.push_back(ln1[0]);
11715         ln.push_back(ln1[1]);
11716         ln.push_back(ln1[2]);
11717         ln.push_back(ln1[3]);
11718         break;
11719       case VTK_QUADRATIC_TRIANGLE:
11720         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11721                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11722         // MESSAGE("vol quad prism " << vol->GetID());
11723         ln.push_back(ln1[0]);
11724         ln.push_back(ln1[1]);
11725         ln.push_back(ln1[2]);
11726         ln.push_back(ln3[0]);
11727         ln.push_back(ln3[1]);
11728         ln.push_back(ln3[2]);
11729         break;
11730       case VTK_QUADRATIC_QUAD:
11731         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11732         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11733         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11734         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11735                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11736                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11737         // MESSAGE("vol quad hexa " << vol->GetID());
11738         ln.push_back(ln1[0]);
11739         ln.push_back(ln1[1]);
11740         ln.push_back(ln1[2]);
11741         ln.push_back(ln1[3]);
11742         ln.push_back(ln3[0]);
11743         ln.push_back(ln3[1]);
11744         ln.push_back(ln3[2]);
11745         ln.push_back(ln3[3]);
11746         break;
11747       case VTK_POLYGON:
11748         break;
11749       default:
11750         break;
11751       }
11752
11753       if (vol)
11754       {
11755         stringstream grpname;
11756         grpname << "jf_";
11757         grpname << idom;
11758         string namegrp = grpname.str();
11759         if (!mapOfJunctionGroups.count(namegrp))
11760           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11761         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11762         if (sgrp)
11763           sgrp->Add(vol->GetID());
11764       }
11765
11766       // --- modify the face
11767
11768       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11769     }
11770   }
11771   return true;
11772 }
11773
11774 /*!
11775  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11776  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11777  *  groups of faces to remove inside the object, (idem edges).
11778  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11779  */
11780 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11781                                       const TopoDS_Shape&             theShape,
11782                                       SMESH_NodeSearcher*             theNodeSearcher,
11783                                       const char*                     groupName,
11784                                       std::vector<double>&            nodesCoords,
11785                                       std::vector<std::vector<int> >& listOfListOfNodes)
11786 {
11787   // MESSAGE("--------------------------------");
11788   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11789   // MESSAGE("--------------------------------");
11790
11791   // --- zone of volumes to remove is given :
11792   //     1 either by a geom shape (one or more vertices) and a radius,
11793   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11794   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11795   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11796   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11797   //     defined by it's name.
11798
11799   SMESHDS_GroupBase* groupDS = 0;
11800   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11801   while ( groupIt->more() )
11802   {
11803     groupDS = 0;
11804     SMESH_Group * group = groupIt->next();
11805     if ( !group ) continue;
11806     groupDS = group->GetGroupDS();
11807     if ( !groupDS || groupDS->IsEmpty() ) continue;
11808     std::string grpName = group->GetName();
11809     //MESSAGE("grpName=" << grpName);
11810     if (grpName == groupName)
11811       break;
11812     else
11813       groupDS = 0;
11814   }
11815
11816   bool isNodeGroup = false;
11817   bool isNodeCoords = false;
11818   if (groupDS)
11819   {
11820     if (groupDS->GetType() != SMDSAbs_Node)
11821       return;
11822     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11823   }
11824
11825   if (nodesCoords.size() > 0)
11826     isNodeCoords = true; // a list o nodes given by their coordinates
11827   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11828
11829   // --- define groups to build
11830
11831   // --- group of SMDS volumes
11832   string grpvName = groupName;
11833   grpvName += "_vol";
11834   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11835   if (!grp)
11836   {
11837     MESSAGE("group not created " << grpvName);
11838     return;
11839   }
11840   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11841
11842   // --- group of SMDS faces on the skin
11843   string grpsName = groupName;
11844   grpsName += "_skin";
11845   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11846   if (!grps)
11847   {
11848     MESSAGE("group not created " << grpsName);
11849     return;
11850   }
11851   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11852
11853   // --- group of SMDS faces internal (several shapes)
11854   string grpiName = groupName;
11855   grpiName += "_internalFaces";
11856   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11857   if (!grpi)
11858   {
11859     MESSAGE("group not created " << grpiName);
11860     return;
11861   }
11862   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11863
11864   // --- group of SMDS faces internal (several shapes)
11865   string grpeiName = groupName;
11866   grpeiName += "_internalEdges";
11867   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11868   if (!grpei)
11869   {
11870     MESSAGE("group not created " << grpeiName);
11871     return;
11872   }
11873   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11874
11875   // --- build downward connectivity
11876
11877   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11878   meshDS->BuildDownWardConnectivity(true);
11879   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11880
11881   // --- set of volumes detected inside
11882
11883   std::set<int> setOfInsideVol;
11884   std::set<int> setOfVolToCheck;
11885
11886   std::vector<gp_Pnt> gpnts;
11887   gpnts.clear();
11888
11889   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11890   {
11891     //MESSAGE("group of nodes provided");
11892     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11893     while ( elemIt->more() )
11894     {
11895       const SMDS_MeshElement* elem = elemIt->next();
11896       if (!elem)
11897         continue;
11898       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11899       if (!node)
11900         continue;
11901       SMDS_MeshElement* vol = 0;
11902       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11903       while (volItr->more())
11904       {
11905         vol = (SMDS_MeshElement*)volItr->next();
11906         setOfInsideVol.insert(vol->GetVtkID());
11907         sgrp->Add(vol->GetID());
11908       }
11909     }
11910   }
11911   else if (isNodeCoords)
11912   {
11913     //MESSAGE("list of nodes coordinates provided");
11914     size_t i = 0;
11915     int k = 0;
11916     while ( i < nodesCoords.size()-2 )
11917     {
11918       double x = nodesCoords[i++];
11919       double y = nodesCoords[i++];
11920       double z = nodesCoords[i++];
11921       gp_Pnt p = gp_Pnt(x, y ,z);
11922       gpnts.push_back(p);
11923       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11924       k++;
11925     }
11926   }
11927   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11928   {
11929     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11930     TopTools_IndexedMapOfShape vertexMap;
11931     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11932     gp_Pnt p = gp_Pnt(0,0,0);
11933     if (vertexMap.Extent() < 1)
11934       return;
11935
11936     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11937     {
11938       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11939       p = BRep_Tool::Pnt(vertex);
11940       gpnts.push_back(p);
11941       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11942     }
11943   }
11944
11945   if (gpnts.size() > 0)
11946   {
11947     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11948     //MESSAGE("startNode->nodeId " << nodeId);
11949
11950     double radius2 = radius*radius;
11951     //MESSAGE("radius2 " << radius2);
11952
11953     // --- volumes on start node
11954
11955     setOfVolToCheck.clear();
11956     SMDS_MeshElement* startVol = 0;
11957     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11958     while (volItr->more())
11959     {
11960       startVol = (SMDS_MeshElement*)volItr->next();
11961       setOfVolToCheck.insert(startVol->GetVtkID());
11962     }
11963     if (setOfVolToCheck.empty())
11964     {
11965       MESSAGE("No volumes found");
11966       return;
11967     }
11968
11969     // --- starting with central volumes then their neighbors, check if they are inside
11970     //     or outside the domain, until no more new neighbor volume is inside.
11971     //     Fill the group of inside volumes
11972
11973     std::map<int, double> mapOfNodeDistance2;
11974     mapOfNodeDistance2.clear();
11975     std::set<int> setOfOutsideVol;
11976     while (!setOfVolToCheck.empty())
11977     {
11978       std::set<int>::iterator it = setOfVolToCheck.begin();
11979       int vtkId = *it;
11980       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11981       bool volInside = false;
11982       vtkIdType npts = 0;
11983       vtkIdType* pts = 0;
11984       grid->GetCellPoints(vtkId, npts, pts);
11985       for (int i=0; i<npts; i++)
11986       {
11987         double distance2 = 0;
11988         if (mapOfNodeDistance2.count(pts[i]))
11989         {
11990           distance2 = mapOfNodeDistance2[pts[i]];
11991           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11992         }
11993         else
11994         {
11995           double *coords = grid->GetPoint(pts[i]);
11996           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11997           distance2 = 1.E40;
11998           for ( size_t j = 0; j < gpnts.size(); j++ )
11999           {
12000             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12001             if (d2 < distance2)
12002             {
12003               distance2 = d2;
12004               if (distance2 < radius2)
12005                 break;
12006             }
12007           }
12008           mapOfNodeDistance2[pts[i]] = distance2;
12009           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12010         }
12011         if (distance2 < radius2)
12012         {
12013           volInside = true; // one or more nodes inside the domain
12014           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12015           break;
12016         }
12017       }
12018       if (volInside)
12019       {
12020         setOfInsideVol.insert(vtkId);
12021         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12022         int neighborsVtkIds[NBMAXNEIGHBORS];
12023         int downIds[NBMAXNEIGHBORS];
12024         unsigned char downTypes[NBMAXNEIGHBORS];
12025         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12026         for (int n = 0; n < nbNeighbors; n++)
12027           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12028             setOfVolToCheck.insert(neighborsVtkIds[n]);
12029       }
12030       else
12031       {
12032         setOfOutsideVol.insert(vtkId);
12033         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12034       }
12035       setOfVolToCheck.erase(vtkId);
12036     }
12037   }
12038
12039   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12040   //     If yes, add the volume to the inside set
12041
12042   bool addedInside = true;
12043   std::set<int> setOfVolToReCheck;
12044   while (addedInside)
12045   {
12046     //MESSAGE(" --------------------------- re check");
12047     addedInside = false;
12048     std::set<int>::iterator itv = setOfInsideVol.begin();
12049     for (; itv != setOfInsideVol.end(); ++itv)
12050     {
12051       int vtkId = *itv;
12052       int neighborsVtkIds[NBMAXNEIGHBORS];
12053       int downIds[NBMAXNEIGHBORS];
12054       unsigned char downTypes[NBMAXNEIGHBORS];
12055       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12056       for (int n = 0; n < nbNeighbors; n++)
12057         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12058           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12059     }
12060     setOfVolToCheck = setOfVolToReCheck;
12061     setOfVolToReCheck.clear();
12062     while  (!setOfVolToCheck.empty())
12063     {
12064       std::set<int>::iterator it = setOfVolToCheck.begin();
12065       int vtkId = *it;
12066       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12067       {
12068         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12069         int countInside = 0;
12070         int neighborsVtkIds[NBMAXNEIGHBORS];
12071         int downIds[NBMAXNEIGHBORS];
12072         unsigned char downTypes[NBMAXNEIGHBORS];
12073         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12074         for (int n = 0; n < nbNeighbors; n++)
12075           if (setOfInsideVol.count(neighborsVtkIds[n]))
12076             countInside++;
12077         //MESSAGE("countInside " << countInside);
12078         if (countInside > 1)
12079         {
12080           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12081           setOfInsideVol.insert(vtkId);
12082           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12083           addedInside = true;
12084         }
12085         else
12086           setOfVolToReCheck.insert(vtkId);
12087       }
12088       setOfVolToCheck.erase(vtkId);
12089     }
12090   }
12091
12092   // --- map of Downward faces at the boundary, inside the global volume
12093   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12094   //     fill group of SMDS faces inside the volume (when several volume shapes)
12095   //     fill group of SMDS faces on the skin of the global volume (if skin)
12096
12097   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12098   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12099   std::set<int>::iterator it = setOfInsideVol.begin();
12100   for (; it != setOfInsideVol.end(); ++it)
12101   {
12102     int vtkId = *it;
12103     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12104     int neighborsVtkIds[NBMAXNEIGHBORS];
12105     int downIds[NBMAXNEIGHBORS];
12106     unsigned char downTypes[NBMAXNEIGHBORS];
12107     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12108     for (int n = 0; n < nbNeighbors; n++)
12109     {
12110       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12111       if (neighborDim == 3)
12112       {
12113         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12114         {
12115           DownIdType face(downIds[n], downTypes[n]);
12116           boundaryFaces[face] = vtkId;
12117         }
12118         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12119         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12120         if (vtkFaceId >= 0)
12121         {
12122           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12123           // find also the smds edges on this face
12124           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12125           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12126           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12127           for (int i = 0; i < nbEdges; i++)
12128           {
12129             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12130             if (vtkEdgeId >= 0)
12131               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12132           }
12133         }
12134       }
12135       else if (neighborDim == 2) // skin of the volume
12136       {
12137         DownIdType face(downIds[n], downTypes[n]);
12138         skinFaces[face] = vtkId;
12139         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12140         if (vtkFaceId >= 0)
12141           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12142       }
12143     }
12144   }
12145
12146   // --- identify the edges constituting the wire of each subshape on the skin
12147   //     define polylines with the nodes of edges, equivalent to wires
12148   //     project polylines on subshapes, and partition, to get geom faces
12149
12150   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12151   std::set<int> emptySet;
12152   emptySet.clear();
12153   std::set<int> shapeIds;
12154
12155   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12156   while (itelem->more())
12157   {
12158     const SMDS_MeshElement *elem = itelem->next();
12159     int shapeId = elem->getshapeId();
12160     int   vtkId = elem->GetVtkID();
12161     if (!shapeIdToVtkIdSet.count(shapeId))
12162     {
12163       shapeIdToVtkIdSet[shapeId] = emptySet;
12164       shapeIds.insert(shapeId);
12165     }
12166     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12167   }
12168
12169   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12170   std::set<DownIdType, DownIdCompare> emptyEdges;
12171   emptyEdges.clear();
12172
12173   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12174   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12175   {
12176     int shapeId = itShape->first;
12177     //MESSAGE(" --- Shape ID --- "<< shapeId);
12178     shapeIdToEdges[shapeId] = emptyEdges;
12179
12180     std::vector<int> nodesEdges;
12181
12182     std::set<int>::iterator its = itShape->second.begin();
12183     for (; its != itShape->second.end(); ++its)
12184     {
12185       int vtkId = *its;
12186       //MESSAGE("     " << vtkId);
12187       int neighborsVtkIds[NBMAXNEIGHBORS];
12188       int downIds[NBMAXNEIGHBORS];
12189       unsigned char downTypes[NBMAXNEIGHBORS];
12190       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12191       for (int n = 0; n < nbNeighbors; n++)
12192       {
12193         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12194           continue;
12195         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12196         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12197         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12198         {
12199           DownIdType edge(downIds[n], downTypes[n]);
12200           if (!shapeIdToEdges[shapeId].count(edge))
12201           {
12202             shapeIdToEdges[shapeId].insert(edge);
12203             int vtkNodeId[3];
12204             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12205             nodesEdges.push_back(vtkNodeId[0]);
12206             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12207             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12208           }
12209         }
12210       }
12211     }
12212
12213     std::list<int> order;
12214     order.clear();
12215     if (nodesEdges.size() > 0)
12216     {
12217       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12218       nodesEdges[0] = -1;
12219       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12220       nodesEdges[1] = -1; // do not reuse this edge
12221       bool found = true;
12222       while (found)
12223       {
12224         int nodeTofind = order.back(); // try first to push back
12225         int i = 0;
12226         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12227           if (nodesEdges[i] == nodeTofind)
12228             break;
12229         if ( i == (int) nodesEdges.size() )
12230           found = false; // no follower found on back
12231         else
12232         {
12233           if (i%2) // odd ==> use the previous one
12234             if (nodesEdges[i-1] < 0)
12235               found = false;
12236             else
12237             {
12238               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12239               nodesEdges[i-1] = -1;
12240             }
12241           else // even ==> use the next one
12242             if (nodesEdges[i+1] < 0)
12243               found = false;
12244             else
12245             {
12246               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12247               nodesEdges[i+1] = -1;
12248             }
12249         }
12250         if (found)
12251           continue;
12252         // try to push front
12253         found = true;
12254         nodeTofind = order.front(); // try to push front
12255         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12256           if ( nodesEdges[i] == nodeTofind )
12257             break;
12258         if ( i == (int)nodesEdges.size() )
12259         {
12260           found = false; // no predecessor found on front
12261           continue;
12262         }
12263         if (i%2) // odd ==> use the previous one
12264           if (nodesEdges[i-1] < 0)
12265             found = false;
12266           else
12267           {
12268             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12269             nodesEdges[i-1] = -1;
12270           }
12271         else // even ==> use the next one
12272           if (nodesEdges[i+1] < 0)
12273             found = false;
12274           else
12275           {
12276             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12277             nodesEdges[i+1] = -1;
12278           }
12279       }
12280     }
12281
12282
12283     std::vector<int> nodes;
12284     nodes.push_back(shapeId);
12285     std::list<int>::iterator itl = order.begin();
12286     for (; itl != order.end(); itl++)
12287     {
12288       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12289       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12290     }
12291     listOfListOfNodes.push_back(nodes);
12292   }
12293
12294   //     partition geom faces with blocFissure
12295   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12296   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12297
12298   return;
12299 }
12300
12301
12302 //================================================================================
12303 /*!
12304  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12305  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12306  * \return TRUE if operation has been completed successfully, FALSE otherwise
12307  */
12308 //================================================================================
12309
12310 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12311 {
12312   // iterates on volume elements and detect all free faces on them
12313   SMESHDS_Mesh* aMesh = GetMeshDS();
12314   if (!aMesh)
12315     return false;
12316
12317   ElemFeatures faceType( SMDSAbs_Face );
12318   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12319   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12320   while(vIt->more())
12321   {
12322     const SMDS_MeshVolume* volume = vIt->next();
12323     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12324     vTool.SetExternalNormal();
12325     const int iQuad = volume->IsQuadratic();
12326     faceType.SetQuad( iQuad );
12327     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12328     {
12329       if (!vTool.IsFreeFace(iface))
12330         continue;
12331       nbFree++;
12332       vector<const SMDS_MeshNode *> nodes;
12333       int nbFaceNodes = vTool.NbFaceNodes(iface);
12334       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12335       int inode = 0;
12336       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12337         nodes.push_back(faceNodes[inode]);
12338
12339       if (iQuad) // add medium nodes
12340       {
12341         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12342           nodes.push_back(faceNodes[inode]);
12343         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12344           nodes.push_back(faceNodes[8]);
12345       }
12346       // add new face based on volume nodes
12347       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12348       {
12349         nbExisted++; // face already exists
12350       }
12351       else
12352       {
12353         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12354         nbCreated++;
12355       }
12356     }
12357   }
12358   return ( nbFree == ( nbExisted + nbCreated ));
12359 }
12360
12361 namespace
12362 {
12363   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12364   {
12365     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12366       return n;
12367     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12368   }
12369 }
12370 //================================================================================
12371 /*!
12372  * \brief Creates missing boundary elements
12373  *  \param elements - elements whose boundary is to be checked
12374  *  \param dimension - defines type of boundary elements to create
12375  *  \param group - a group to store created boundary elements in
12376  *  \param targetMesh - a mesh to store created boundary elements in
12377  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12378  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12379  *                                boundary elements will be copied into the targetMesh
12380  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12381  *                                boundary elements will be added into the new group
12382  *  \param aroundElements - if true, elements will be created on boundary of given
12383  *                          elements else, on boundary of the whole mesh.
12384  * \return nb of added boundary elements
12385  */
12386 //================================================================================
12387
12388 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12389                                        Bnd_Dimension           dimension,
12390                                        SMESH_Group*            group/*=0*/,
12391                                        SMESH_Mesh*             targetMesh/*=0*/,
12392                                        bool                    toCopyElements/*=false*/,
12393                                        bool                    toCopyExistingBoundary/*=false*/,
12394                                        bool                    toAddExistingBondary/*= false*/,
12395                                        bool                    aroundElements/*= false*/)
12396 {
12397   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12398   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12399   // hope that all elements are of the same type, do not check them all
12400   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12401     throw SALOME_Exception(LOCALIZED("wrong element type"));
12402
12403   if ( !targetMesh )
12404     toCopyElements = toCopyExistingBoundary = false;
12405
12406   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12407   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12408   int nbAddedBnd = 0;
12409
12410   // editor adding present bnd elements and optionally holding elements to add to the group
12411   SMESH_MeshEditor* presentEditor;
12412   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12413   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12414
12415   SMESH_MesherHelper helper( *myMesh );
12416   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12417   SMDS_VolumeTool vTool;
12418   TIDSortedElemSet avoidSet;
12419   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12420   size_t inode;
12421
12422   typedef vector<const SMDS_MeshNode*> TConnectivity;
12423   TConnectivity tgtNodes;
12424   ElemFeatures elemKind( missType ), elemToCopy;
12425
12426   vector<const SMDS_MeshElement*> presentBndElems;
12427   vector<TConnectivity>           missingBndElems;
12428   vector<int>                     freeFacets;
12429   TConnectivity nodes, elemNodes;
12430
12431   SMDS_ElemIteratorPtr eIt;
12432   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12433   else                  eIt = SMESHUtils::elemSetIterator( elements );
12434
12435   while ( eIt->more() )
12436   {
12437     const SMDS_MeshElement* elem = eIt->next();
12438     const int              iQuad = elem->IsQuadratic();
12439     elemKind.SetQuad( iQuad );
12440
12441     // ------------------------------------------------------------------------------------
12442     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12443     // ------------------------------------------------------------------------------------
12444     presentBndElems.clear();
12445     missingBndElems.clear();
12446     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12447     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12448     {
12449       const SMDS_MeshElement* otherVol = 0;
12450       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12451       {
12452         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12453              ( !aroundElements || elements.count( otherVol )))
12454           continue;
12455         freeFacets.push_back( iface );
12456       }
12457       if ( missType == SMDSAbs_Face )
12458         vTool.SetExternalNormal();
12459       for ( size_t i = 0; i < freeFacets.size(); ++i )
12460       {
12461         int                iface = freeFacets[i];
12462         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12463         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12464         if ( missType == SMDSAbs_Edge ) // boundary edges
12465         {
12466           nodes.resize( 2+iQuad );
12467           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12468           {
12469             for ( size_t j = 0; j < nodes.size(); ++j )
12470               nodes[ j ] = nn[ i+j ];
12471             if ( const SMDS_MeshElement* edge =
12472                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12473               presentBndElems.push_back( edge );
12474             else
12475               missingBndElems.push_back( nodes );
12476           }
12477         }
12478         else // boundary face
12479         {
12480           nodes.clear();
12481           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12482             nodes.push_back( nn[inode] ); // add corner nodes
12483           if (iQuad)
12484             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12485               nodes.push_back( nn[inode] ); // add medium nodes
12486           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12487           if ( iCenter > 0 )
12488             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12489
12490           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12491                                                                SMDSAbs_Face, /*noMedium=*/false ))
12492             presentBndElems.push_back( f );
12493           else
12494             missingBndElems.push_back( nodes );
12495
12496           if ( targetMesh != myMesh )
12497           {
12498             // add 1D elements on face boundary to be added to a new mesh
12499             const SMDS_MeshElement* edge;
12500             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12501             {
12502               if ( iQuad )
12503                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12504               else
12505                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12506               if ( edge && avoidSet.insert( edge ).second )
12507                 presentBndElems.push_back( edge );
12508             }
12509           }
12510         }
12511       }
12512     }
12513     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12514     {
12515       avoidSet.clear(), avoidSet.insert( elem );
12516       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12517                         SMDS_MeshElement::iterator() );
12518       elemNodes.push_back( elemNodes[0] );
12519       nodes.resize( 2 + iQuad );
12520       const int nbLinks = elem->NbCornerNodes();
12521       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12522       {
12523         nodes[0] = elemNodes[iN];
12524         nodes[1] = elemNodes[iN+1+iQuad];
12525         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12526           continue; // not free link
12527
12528         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12529         if ( const SMDS_MeshElement* edge =
12530              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12531           presentBndElems.push_back( edge );
12532         else
12533           missingBndElems.push_back( nodes );
12534       }
12535     }
12536
12537     // ---------------------------------
12538     // 2. Add missing boundary elements
12539     // ---------------------------------
12540     if ( targetMesh != myMesh )
12541       // instead of making a map of nodes in this mesh and targetMesh,
12542       // we create nodes with same IDs.
12543       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12544       {
12545         TConnectivity& srcNodes = missingBndElems[i];
12546         tgtNodes.resize( srcNodes.size() );
12547         for ( inode = 0; inode < srcNodes.size(); ++inode )
12548           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12549         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12550                                                                    missType,
12551                                                                    /*noMedium=*/false))
12552           continue;
12553         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12554         ++nbAddedBnd;
12555       }
12556     else
12557       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12558       {
12559         TConnectivity& nodes = missingBndElems[ i ];
12560         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12561                                                                    missType,
12562                                                                    /*noMedium=*/false))
12563           continue;
12564         SMDS_MeshElement* newElem =
12565           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12566         nbAddedBnd += bool( newElem );
12567
12568         // try to set a new element to a shape
12569         if ( myMesh->HasShapeToMesh() )
12570         {
12571           bool ok = true;
12572           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12573           const size_t nbN = nodes.size() / (iQuad+1 );
12574           for ( inode = 0; inode < nbN && ok; ++inode )
12575           {
12576             pair<int, TopAbs_ShapeEnum> i_stype =
12577               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12578             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12579               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12580           }
12581           if ( ok && mediumShapes.size() > 1 )
12582           {
12583             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12584             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12585             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12586             {
12587               if (( ok = ( stype_i->first != stype_i_0.first )))
12588                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12589                                         aMesh->IndexToShape( stype_i_0.second ));
12590             }
12591           }
12592           if ( ok && mediumShapes.begin()->first == missShapeType )
12593             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12594         }
12595       }
12596
12597     // ----------------------------------
12598     // 3. Copy present boundary elements
12599     // ----------------------------------
12600     if ( toCopyExistingBoundary )
12601       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12602       {
12603         const SMDS_MeshElement* e = presentBndElems[i];
12604         tgtNodes.resize( e->NbNodes() );
12605         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12606           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12607         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12608       }
12609     else // store present elements to add them to a group
12610       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12611       {
12612         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12613       }
12614
12615   } // loop on given elements
12616
12617   // ---------------------------------------------
12618   // 4. Fill group with boundary elements
12619   // ---------------------------------------------
12620   if ( group )
12621   {
12622     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12623       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12624         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12625   }
12626   tgtEditor.myLastCreatedElems.clear();
12627   tgtEditor2.myLastCreatedElems.clear();
12628
12629   // -----------------------
12630   // 5. Copy given elements
12631   // -----------------------
12632   if ( toCopyElements && targetMesh != myMesh )
12633   {
12634     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12635     else                  eIt = SMESHUtils::elemSetIterator( elements );
12636     while (eIt->more())
12637     {
12638       const SMDS_MeshElement* elem = eIt->next();
12639       tgtNodes.resize( elem->NbNodes() );
12640       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12641         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12642       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12643
12644       tgtEditor.myLastCreatedElems.clear();
12645     }
12646   }
12647   return nbAddedBnd;
12648 }
12649
12650 //================================================================================
12651 /*!
12652  * \brief Copy node position and set \a to node on the same geometry
12653  */
12654 //================================================================================
12655
12656 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12657                                      const SMDS_MeshNode* to )
12658 {
12659   if ( !from || !to ) return;
12660
12661   SMDS_PositionPtr pos = from->GetPosition();
12662   if ( !pos || from->getshapeId() < 1 ) return;
12663
12664   switch ( pos->GetTypeOfPosition() )
12665   {
12666   case SMDS_TOP_3DSPACE: break;
12667
12668   case SMDS_TOP_FACE:
12669   {
12670     SMDS_FacePositionPtr fPos = pos;
12671     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12672                                 fPos->GetUParameter(), fPos->GetVParameter() );
12673     break;
12674   }
12675   case SMDS_TOP_EDGE:
12676   {
12677     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12678     SMDS_EdgePositionPtr ePos = pos;
12679     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12680     break;
12681   }
12682   case SMDS_TOP_VERTEX:
12683   {
12684     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12685     break;
12686   }
12687   case SMDS_TOP_UNSPEC:
12688   default:;
12689   }
12690 }