Salome HOME
#18782 EDF 20946 - Free nodes with biquadratic
[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           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165         }
166       }
167     }
168     else if ( myType == SMDSAbs_Ball && !basicOnly )
169     {
170       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
171     }
172   }
173   return *this;
174 }
175
176 //=======================================================================
177 /*!
178  * \brief Add element
179  */
180 //=======================================================================
181
182 SMDS_MeshElement*
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184                              const ElemFeatures&                  features)
185 {
186   SMDS_MeshElement* e = 0;
187   int nbnode = node.size();
188   SMESHDS_Mesh* mesh = GetMeshDS();
189   const int ID = features.myID;
190
191   switch ( features.myType ) {
192   case SMDSAbs_Face:
193     if ( !features.myIsPoly ) {
194       if      (nbnode == 3) {
195         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196         else           e = mesh->AddFace      (node[0], node[1], node[2] );
197       }
198       else if (nbnode == 4) {
199         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204                                                node[4], node[5], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
206                                                node[4], node[5] );
207       }
208       else if (nbnode == 7) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210                                                node[4], node[5], node[6], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6] );
213       }
214       else if (nbnode == 8) {
215         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216                                                node[4], node[5], node[6], node[7], ID);
217         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7] );
219       }
220       else if (nbnode == 9) {
221         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], node[7], node[8], ID);
223         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8] );
225       }
226     }
227     else if ( !features.myIsQuad )
228     {
229       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230       else           e = mesh->AddPolygonalFace      (node    );
231     }
232     else if ( nbnode % 2 == 0 ) // just a protection
233     {
234       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235       else           e = mesh->AddQuadPolygonalFace      (node    );
236     }
237     break;
238
239   case SMDSAbs_Volume:
240     if ( !features.myIsPoly ) {
241       if      (nbnode == 4) {
242         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
244       }
245       else if (nbnode == 5) {
246         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247                                                  node[4], ID);
248         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
249                                                  node[4] );
250       }
251       else if (nbnode == 6) {
252         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253                                                  node[4], node[5], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5] );
256       }
257       else if (nbnode == 8) {
258         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259                                                  node[4], node[5], node[6], node[7], ID);
260         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7] );
262       }
263       else if (nbnode == 10) {
264         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7],
269                                                  node[8], node[9] );
270       }
271       else if (nbnode == 12) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10], node[11], ID);
275         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], node[10], node[11] );
278       }
279       else if (nbnode == 13) {
280         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281                                                  node[4], node[5], node[6], node[7],
282                                                  node[8], node[9], node[10],node[11],
283                                                  node[12],ID);
284         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10],node[11],
287                                                  node[12] );
288       }
289       else if (nbnode == 15) {
290         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291                                                  node[4], node[5], node[6], node[7],
292                                                  node[8], node[9], node[10],node[11],
293                                                  node[12],node[13],node[14],ID);
294         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],node[13],node[14] );
298       }
299       else if (nbnode == 18) {
300         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301                                                  node[4], node[5], node[6], node[7],
302                                                  node[8], node[9], node[10],node[11],
303                                                  node[12],node[13],node[14],
304                                                  node[15],node[16],node[17],ID );
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14],
309                                                  node[15],node[16],node[17] );
310       }
311       else if (nbnode == 20) {
312         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313                                                  node[4], node[5], node[6], node[7],
314                                                  node[8], node[9], node[10],node[11],
315                                                  node[12],node[13],node[14],node[15],
316                                                  node[16],node[17],node[18],node[19],ID);
317         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
318                                                  node[4], node[5], node[6], node[7],
319                                                  node[8], node[9], node[10],node[11],
320                                                  node[12],node[13],node[14],node[15],
321                                                  node[16],node[17],node[18],node[19] );
322       }
323       else if (nbnode == 27) {
324         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325                                                  node[4], node[5], node[6], node[7],
326                                                  node[8], node[9], node[10],node[11],
327                                                  node[12],node[13],node[14],node[15],
328                                                  node[16],node[17],node[18],node[19],
329                                                  node[20],node[21],node[22],node[23],
330                                                  node[24],node[25],node[26], ID);
331         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
332                                                  node[4], node[5], node[6], node[7],
333                                                  node[8], node[9], node[10],node[11],
334                                                  node[12],node[13],node[14],node[15],
335                                                  node[16],node[17],node[18],node[19],
336                                                  node[20],node[21],node[22],node[23],
337                                                  node[24],node[25],node[26] );
338       }
339     }
340     else if ( !features.myIsQuad )
341     {
342       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
344     }
345     else
346     {
347       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
349     }
350     break;
351
352   case SMDSAbs_Edge:
353     if ( nbnode == 2 ) {
354       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355       else           e = mesh->AddEdge      (node[0], node[1] );
356     }
357     else if ( nbnode == 3 ) {
358       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
360     }
361     break;
362
363   case SMDSAbs_0DElement:
364     if ( nbnode == 1 ) {
365       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366       else           e = mesh->Add0DElement      (node[0] );
367     }
368     break;
369
370   case SMDSAbs_Node:
371     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
373     break;
374
375   case SMDSAbs_Ball:
376     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
378     break;
379
380   default:;
381   }
382   if ( e ) myLastCreatedElems.push_back( e );
383   return e;
384 }
385
386 //=======================================================================
387 /*!
388  * \brief Add element
389  */
390 //=======================================================================
391
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393                                                const ElemFeatures& features)
394 {
395   vector<const SMDS_MeshNode*> nodes;
396   nodes.reserve( nodeIDs.size() );
397   vector<int>::const_iterator id = nodeIDs.begin();
398   while ( id != nodeIDs.end() ) {
399     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400       nodes.push_back( node );
401     else
402       return 0;
403   }
404   return AddElement( nodes, features );
405 }
406
407 //=======================================================================
408 //function : Remove
409 //purpose  : Remove a node or an element.
410 //           Modify a compute state of sub-meshes which become empty
411 //=======================================================================
412
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
414                               const bool         isNodes )
415 {
416   ClearLastCreated();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = SMESHUtils::elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.back() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   ClearLastCreated();
527
528   SMESHDS_Mesh * aMesh = GetMeshDS();
529   if ( aMesh->ShapeToMesh().IsNull() )
530     return 0;
531
532   int aShapeID = theElem->getshapeId();
533   if ( aShapeID < 1 )
534     return 0;
535
536   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537     if ( sm->Contains( theElem ))
538       return aShapeID;
539
540   if ( theElem->GetType() == SMDSAbs_Node ) {
541     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
542   }
543   else {
544     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
545   }
546
547   TopoDS_Shape aShape; // the shape a node of theElem is on
548   if ( theElem->GetType() != SMDSAbs_Node )
549   {
550     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551     while ( nodeIt->more() ) {
552       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553       if ((aShapeID = node->getshapeId()) > 0) {
554         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555           if ( sm->Contains( theElem ))
556             return aShapeID;
557           if ( aShape.IsNull() )
558             aShape = aMesh->IndexToShape( aShapeID );
559         }
560       }
561     }
562   }
563
564   // None of nodes is on a proper shape,
565   // find the shape among ancestors of aShape on which a node is
566   if ( !aShape.IsNull() ) {
567     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568     for ( ; ancIt.More(); ancIt.Next() ) {
569       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570       if ( sm && sm->Contains( theElem ))
571         return aMesh->ShapeToIndex( ancIt.Value() );
572     }
573   }
574   else
575   {
576     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577     while ( const SMESHDS_SubMesh* sm = smIt->next() )
578       if ( sm->Contains( theElem ))
579         return sm->GetID();
580   }
581
582   return 0;
583 }
584
585 //=======================================================================
586 //function : IsMedium
587 //purpose  :
588 //=======================================================================
589
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
591                                 const SMDSAbs_ElementType typeToCheck)
592 {
593   bool isMedium = false;
594   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595   while (it->more() && !isMedium ) {
596     const SMDS_MeshElement* elem = it->next();
597     isMedium = elem->IsMediumNode(node);
598   }
599   return isMedium;
600 }
601
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose  : Shift nodes in the array corresponded to quadratic triangle
605 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
607
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
609 {
610   const SMDS_MeshNode* nd1 = aNodes[0];
611   aNodes[0] = aNodes[1];
612   aNodes[1] = aNodes[2];
613   aNodes[2] = nd1;
614   const SMDS_MeshNode* nd2 = aNodes[3];
615   aNodes[3] = aNodes[4];
616   aNodes[4] = aNodes[5];
617   aNodes[5] = nd2;
618 }
619
620 //=======================================================================
621 //function : getNodesFromTwoTria
622 //purpose  : 
623 //=======================================================================
624
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626                                 const SMDS_MeshElement * theTria2,
627                                 vector< const SMDS_MeshNode*>& N1,
628                                 vector< const SMDS_MeshNode*>& N2)
629 {
630   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631   if ( N1.size() < 6 ) return false;
632   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633   if ( N2.size() < 6 ) return false;
634
635   int sames[3] = {-1,-1,-1};
636   int nbsames = 0;
637   int i, j;
638   for(i=0; i<3; i++) {
639     for(j=0; j<3; j++) {
640       if(N1[i]==N2[j]) {
641         sames[i] = j;
642         nbsames++;
643         break;
644       }
645     }
646   }
647   if(nbsames!=2) return false;
648   if(sames[0]>-1) {
649     shiftNodesQuadTria(N1);
650     if(sames[1]>-1) {
651       shiftNodesQuadTria(N1);
652     }
653   }
654   i = sames[0] + sames[1] + sames[2];
655   for(; i<2; i++) {
656     shiftNodesQuadTria(N2);
657   }
658   // now we receive following N1 and N2 (using numeration as in the image below)
659   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
660   // i.e. first nodes from both arrays form a new diagonal
661   return true;
662 }
663
664 //=======================================================================
665 //function : InverseDiag
666 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
667 //           but having other common link.
668 //           Return False if args are improper
669 //=======================================================================
670
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672                                     const SMDS_MeshElement * theTria2 )
673 {
674   ClearLastCreated();
675
676   if ( !theTria1 || !theTria2 ||
677        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679        theTria1->GetType() != SMDSAbs_Face ||
680        theTria2->GetType() != SMDSAbs_Face )
681     return false;
682
683   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684       (theTria2->GetEntityType() == SMDSEntity_Triangle))
685   {
686     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
688     //    |/ |                                         | \|
689     //  B +--+ 2                                     B +--+ 2
690
691     // put nodes in array and find out indices of the same ones
692     const SMDS_MeshNode* aNodes [6];
693     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
694     int i = 0;
695     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696     while ( it->more() ) {
697       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
698
699       if ( i > 2 ) // theTria2
700         // find same node of theTria1
701         for ( int j = 0; j < 3; j++ )
702           if ( aNodes[ i ] == aNodes[ j ]) {
703             sameInd[ j ] = i;
704             sameInd[ i ] = j;
705             break;
706           }
707       // next
708       i++;
709       if ( i == 3 ) {
710         if ( it->more() )
711           return false; // theTria1 is not a triangle
712         it = theTria2->nodesIterator();
713       }
714       if ( i == 6 && it->more() )
715         return false; // theTria2 is not a triangle
716     }
717
718     // find indices of 1,2 and of A,B in theTria1
719     int iA = -1, iB = 0, i1 = 0, i2 = 0;
720     for ( i = 0; i < 6; i++ ) {
721       if ( sameInd [ i ] == -1 ) {
722         if ( i < 3 ) i1 = i;
723         else         i2 = i;
724       }
725       else if (i < 3) {
726         if ( iA >= 0) iB = i;
727         else          iA = i;
728       }
729     }
730     // nodes 1 and 2 should not be the same
731     if ( aNodes[ i1 ] == aNodes[ i2 ] )
732       return false;
733
734     // theTria1: A->2
735     aNodes[ iA ] = aNodes[ i2 ];
736     // theTria2: B->1
737     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
738
739     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
741
742     return true;
743
744   } // end if(F1 && F2)
745
746   // check case of quadratic faces
747   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
749     return false;
750   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
752     return false;
753
754   //       5
755   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
757   //    |   / |
758   //  7 +  +  + 6
759   //    | /9  |
760   //    |/    |
761   //  4 +--+--+ 3
762   //       8
763
764   vector< const SMDS_MeshNode* > N1;
765   vector< const SMDS_MeshNode* > N2;
766   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
767     return false;
768   // now we receive following N1 and N2 (using numeration as above image)
769   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
770   // i.e. first nodes from both arrays determ new diagonal
771
772   vector< const SMDS_MeshNode*> N1new( N1.size() );
773   vector< const SMDS_MeshNode*> N2new( N2.size() );
774   N1new.back() = N1.back(); // central node of biquadratic
775   N2new.back() = N2.back();
776   N1new[0] = N1[0];  N2new[0] = N1[0];
777   N1new[1] = N2[0];  N2new[1] = N1[1];
778   N1new[2] = N2[1];  N2new[2] = N2[0];
779   N1new[3] = N1[4];  N2new[3] = N1[3];
780   N1new[4] = N2[3];  N2new[4] = N2[5];
781   N1new[5] = N1[5];  N2new[5] = N1[4];
782   // change nodes in faces
783   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
785
786   // move the central node of biquadratic triangle
787   SMESH_MesherHelper helper( *GetMesh() );
788   for ( int is2nd = 0; is2nd < 2; ++is2nd )
789   {
790     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
791     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792     if ( nodes.size() < 7 )
793       continue;
794     helper.SetSubShape( tria->getshapeId() );
795     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
796     gp_Pnt xyz;
797     if ( F.IsNull() )
798     {
799       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800               SMESH_NodeXYZ( nodes[4] ) +
801               SMESH_NodeXYZ( nodes[5] )) / 3.;
802     }
803     else
804     {
805       bool checkUV;
806       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
809       TopLoc_Location loc;
810       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811       xyz = S->Value( uv.X(), uv.Y() );
812       xyz.Transform( loc );
813       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
814            nodes[6]->getshapeId() > 0 )
815         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
816     }
817     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
818   }
819   return true;
820 }
821
822 //=======================================================================
823 //function : findTriangles
824 //purpose  : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
826
827 static bool findTriangles(const SMDS_MeshNode *    theNode1,
828                           const SMDS_MeshNode *    theNode2,
829                           const SMDS_MeshElement*& theTria1,
830                           const SMDS_MeshElement*& theTria2)
831 {
832   if ( !theNode1 || !theNode2 ) return false;
833
834   theTria1 = theTria2 = 0;
835
836   set< const SMDS_MeshElement* > emap;
837   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
838   while (it->more()) {
839     const SMDS_MeshElement* elem = it->next();
840     if ( elem->NbCornerNodes() == 3 )
841       emap.insert( elem );
842   }
843   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
844   while (it->more()) {
845     const SMDS_MeshElement* elem = it->next();
846     if ( emap.count( elem )) {
847       if ( !theTria1 )
848       {
849         theTria1 = elem;
850       }
851       else  
852       {
853         theTria2 = elem;
854         // theTria1 must be element with minimum ID
855         if ( theTria2->GetID() < theTria1->GetID() )
856           std::swap( theTria2, theTria1 );
857         return true;
858       }
859     }
860   }
861   return false;
862 }
863
864 //=======================================================================
865 //function : InverseDiag
866 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
867 //           with ones built on the same 4 nodes but having other common link.
868 //           Return false if proper faces not found
869 //=======================================================================
870
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872                                     const SMDS_MeshNode * theNode2)
873 {
874   ClearLastCreated();
875
876   const SMDS_MeshElement *tr1, *tr2;
877   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
878     return false;
879
880   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
882     return false;
883
884   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
886
887     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
889     //    |/ |                                    | \|
890     //  B +--+ 2                                B +--+ 2
891
892     // put nodes in array
893     // and find indices of 1,2 and of A in tr1 and of B in tr2
894     int i, iA1 = 0, i1 = 0;
895     const SMDS_MeshNode* aNodes1 [3];
896     SMDS_ElemIteratorPtr it;
897     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899       if ( aNodes1[ i ] == theNode1 )
900         iA1 = i; // node A in tr1
901       else if ( aNodes1[ i ] != theNode2 )
902         i1 = i;  // node 1
903     }
904     int iB2 = 0, i2 = 0;
905     const SMDS_MeshNode* aNodes2 [3];
906     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes2[ i ] == theNode2 )
909         iB2 = i; // node B in tr2
910       else if ( aNodes2[ i ] != theNode1 )
911         i2 = i;  // node 2
912     }
913
914     // nodes 1 and 2 should not be the same
915     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
916       return false;
917
918     // tr1: A->2
919     aNodes1[ iA1 ] = aNodes2[ i2 ];
920     // tr2: B->1
921     aNodes2[ iB2 ] = aNodes1[ i1 ];
922
923     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
925
926     return true;
927   }
928
929   // check case of quadratic faces
930   return InverseDiag(tr1,tr2);
931 }
932
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
936 //           fusion of triangles tr1 and tr2 having shared link on
937 //           theNode1 and theNode2
938 //=======================================================================
939
940 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
941                         const SMDS_MeshNode *    theNode1,
942                         const SMDS_MeshNode *    theNode2,
943                         const SMDS_MeshElement * tr1,
944                         const SMDS_MeshElement * tr2 )
945 {
946   if( tr1->NbNodes() != tr2->NbNodes() )
947     return false;
948   // find the 4-th node to insert into tr1
949   const SMDS_MeshNode* n4 = 0;
950   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
951   int i=0;
952   while ( !n4 && i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( !isDiag )
957       n4 = n;
958   }
959   // Make an array of nodes to be in a quadrangle
960   int iNode = 0, iFirstDiag = -1;
961   it = tr1->nodesIterator();
962   i=0;
963   while ( i<3 ) {
964     const SMDS_MeshNode * n = cast2Node( it->next() );
965     i++;
966     bool isDiag = ( n == theNode1 || n == theNode2 );
967     if ( isDiag ) {
968       if ( iFirstDiag < 0 )
969         iFirstDiag = iNode;
970       else if ( iNode - iFirstDiag == 1 )
971         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
972     }
973     else if ( n == n4 ) {
974       return false; // tr1 and tr2 should not have all the same nodes
975     }
976     theQuadNodes[ iNode++ ] = n;
977   }
978   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979     theQuadNodes[ iNode ] = n4;
980
981   return true;
982 }
983
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
987 //           with a quadrangle built on the same 4 nodes.
988 //           Return false if proper faces not found
989 //=======================================================================
990
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992                                    const SMDS_MeshNode * theNode2)
993 {
994   ClearLastCreated();
995
996   const SMDS_MeshElement *tr1, *tr2;
997   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
998     return false;
999
1000   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1002     return false;
1003
1004   SMESHDS_Mesh * aMesh = GetMeshDS();
1005
1006   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007       (tr2->GetEntityType() == SMDSEntity_Triangle))
1008   {
1009     const SMDS_MeshNode* aNodes [ 4 ];
1010     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1011       return false;
1012
1013     const SMDS_MeshElement* newElem = 0;
1014     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015     myLastCreatedElems.push_back(newElem);
1016     AddToSameGroups( newElem, tr1, aMesh );
1017     int aShapeId = tr1->getshapeId();
1018     if ( aShapeId )
1019       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1020
1021     aMesh->RemoveElement( tr1 );
1022     aMesh->RemoveElement( tr2 );
1023
1024     return true;
1025   }
1026
1027   // check case of quadratic faces
1028   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1029     return false;
1030   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1031     return false;
1032
1033   //       5
1034   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1036   //    |   / |
1037   //  7 +  +  + 6
1038   //    | /9  |
1039   //    |/    |
1040   //  4 +--+--+ 3
1041   //       8
1042
1043   vector< const SMDS_MeshNode* > N1;
1044   vector< const SMDS_MeshNode* > N2;
1045   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1046     return false;
1047   // now we receive following N1 and N2 (using numeration as above image)
1048   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1049   // i.e. first nodes from both arrays determ new diagonal
1050
1051   const SMDS_MeshNode* aNodes[8];
1052   aNodes[0] = N1[0];
1053   aNodes[1] = N1[1];
1054   aNodes[2] = N2[0];
1055   aNodes[3] = N2[1];
1056   aNodes[4] = N1[3];
1057   aNodes[5] = N2[5];
1058   aNodes[6] = N2[3];
1059   aNodes[7] = N1[5];
1060
1061   const SMDS_MeshElement* newElem = 0;
1062   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064   myLastCreatedElems.push_back(newElem);
1065   AddToSameGroups( newElem, tr1, aMesh );
1066   int aShapeId = tr1->getshapeId();
1067   if ( aShapeId )
1068   {
1069     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1070   }
1071   aMesh->RemoveElement( tr1 );
1072   aMesh->RemoveElement( tr2 );
1073
1074   // remove middle node (9)
1075   GetMeshDS()->RemoveNode( N1[4] );
1076
1077   return true;
1078 }
1079
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose  : Reverse theElement orientation
1083 //=======================================================================
1084
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1086 {
1087   ClearLastCreated();
1088
1089   if (!theElem)
1090     return false;
1091   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092   if ( !it || !it->more() )
1093     return false;
1094
1095   const SMDSAbs_ElementType type = theElem->GetType();
1096   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1097     return false;
1098
1099   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1101   {
1102     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1103     if (!aPolyedre) {
1104       MESSAGE("Warning: bad volumic element");
1105       return false;
1106     }
1107     SMDS_VolumeTool vTool( aPolyedre );
1108     const int nbFaces = vTool.NbFaces();
1109     vector<int> quantities( nbFaces );
1110     vector<const SMDS_MeshNode *> poly_nodes;
1111
1112     // check if all facets are oriented equally
1113     bool sameOri = true;
1114     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115     for (int iface = 0; iface < nbFaces; iface++)
1116     {
1117       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118       if ( facetOri[ iface ] != facetOri[ 0 ])
1119         sameOri = false;
1120     }
1121
1122     // reverse faces of the polyhedron
1123     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124     poly_nodes.reserve( vTool.NbNodes() );
1125     for ( int iface = 0; iface < nbFaces; iface++ )
1126     {
1127       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1128       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129       bool toReverse = ( facetOri[ iface ] != neededOri );
1130
1131       quantities[ iface ] = nbFaceNodes;
1132
1133       if ( toReverse )
1134         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135           poly_nodes.push_back( nodes[ inode ]);
1136       else
1137         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1138     }
1139     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1140   }
1141   else // other elements
1142   {
1143     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145     if ( interlace.empty() )
1146     {
1147       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1148     }
1149     else
1150     {
1151       SMDS_MeshCell::applyInterlace( interlace, nodes );
1152     }
1153     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1154   }
1155   return false;
1156 }
1157
1158 //================================================================================
1159 /*!
1160  * \brief Reorient faces.
1161  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162  * \param theDirection - desired direction of normal of \a theFace
1163  * \param theFace - one of \a theFaces that should be oriented according to
1164  *        \a theDirection and whose orientation defines orientation of other faces
1165  * \return number of reoriented faces.
1166  */
1167 //================================================================================
1168
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1170                                   const gp_Dir&            theDirection,
1171                                   const SMDS_MeshElement * theFace)
1172 {
1173   int nbReori = 0;
1174   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1175
1176   if ( theFaces.empty() )
1177   {
1178     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179     while ( fIt->more() )
1180       theFaces.insert( theFaces.end(), fIt->next() );
1181   }
1182
1183   // orient theFace according to theDirection
1184   gp_XYZ normal;
1185   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186   if ( normal * theDirection.XYZ() < 0 )
1187     nbReori += Reorient( theFace );
1188
1189   // Orient other faces
1190
1191   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192   TIDSortedElemSet avoidSet;
1193   set< SMESH_TLink > checkedLinks;
1194   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1195
1196   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197     theFaces.erase( theFace );
1198   startFaces.insert( theFace );
1199
1200   int nodeInd1, nodeInd2;
1201   const SMDS_MeshElement*           otherFace;
1202   vector< const SMDS_MeshElement* > facesNearLink;
1203   vector< std::pair< int, int > >   nodeIndsOfFace;
1204
1205   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206   while ( !startFaces.empty() )
1207   {
1208     startFace = startFaces.begin();
1209     theFace = *startFace;
1210     startFaces.erase( startFace );
1211     if ( !visitedFaces.insert( theFace ).second )
1212       continue;
1213
1214     avoidSet.clear();
1215     avoidSet.insert(theFace);
1216
1217     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1218
1219     const int nbNodes = theFace->NbCornerNodes();
1220     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1221     {
1222       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223       linkIt_isNew = checkedLinks.insert( link );
1224       if ( !linkIt_isNew.second )
1225       {
1226         // link has already been checked and won't be encountered more
1227         // if the group (theFaces) is manifold
1228         //checkedLinks.erase( linkIt_isNew.first );
1229       }
1230       else
1231       {
1232         facesNearLink.clear();
1233         nodeIndsOfFace.clear();
1234         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1235                                                              theFaces, avoidSet,
1236                                                              &nodeInd1, &nodeInd2 )))
1237           if ( otherFace != theFace)
1238           {
1239             facesNearLink.push_back( otherFace );
1240             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241             avoidSet.insert( otherFace );
1242           }
1243         if ( facesNearLink.size() > 1 )
1244         {
1245           // NON-MANIFOLD mesh shell !
1246           // select a face most co-directed with theFace,
1247           // other faces won't be visited this time
1248           gp_XYZ NF, NOF;
1249           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250           double proj, maxProj = -1;
1251           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253             if (( proj = Abs( NF * NOF )) > maxProj ) {
1254               maxProj = proj;
1255               otherFace = facesNearLink[i];
1256               nodeInd1  = nodeIndsOfFace[i].first;
1257               nodeInd2  = nodeIndsOfFace[i].second;
1258             }
1259           }
1260           // not to visit rejected faces
1261           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263               visitedFaces.insert( facesNearLink[i] );
1264         }
1265         else if ( facesNearLink.size() == 1 )
1266         {
1267           otherFace = facesNearLink[0];
1268           nodeInd1  = nodeIndsOfFace.back().first;
1269           nodeInd2  = nodeIndsOfFace.back().second;
1270         }
1271         if ( otherFace && otherFace != theFace)
1272         {
1273           // link must be reverse in otherFace if orientation to otherFace
1274           // is same as that of theFace
1275           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1276           {
1277             nbReori += Reorient( otherFace );
1278           }
1279           startFaces.insert( otherFace );
1280         }
1281       }
1282       std::swap( link.first, link.second ); // reverse the link
1283     }
1284   }
1285   return nbReori;
1286 }
1287
1288 //================================================================================
1289 /*!
1290  * \brief Reorient faces basing on orientation of adjacent volumes.
1291  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292  * \param theVolumes - reference volumes.
1293  * \param theOutsideNormal - to orient faces to have their normal
1294  *        pointing either \a outside or \a inside the adjacent volumes.
1295  * \return number of reoriented faces.
1296  */
1297 //================================================================================
1298
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300                                       TIDSortedElemSet & theVolumes,
1301                                       const bool         theOutsideNormal)
1302 {
1303   int nbReori = 0;
1304
1305   SMDS_ElemIteratorPtr faceIt;
1306   if ( theFaces.empty() )
1307     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1308   else
1309     faceIt = SMESHUtils::elemSetIterator( theFaces );
1310
1311   vector< const SMDS_MeshNode* > faceNodes;
1312   TIDSortedElemSet checkedVolumes;
1313   set< const SMDS_MeshNode* > faceNodesSet;
1314   SMDS_VolumeTool volumeTool;
1315
1316   while ( faceIt->more() ) // loop on given faces
1317   {
1318     const SMDS_MeshElement* face = faceIt->next();
1319     if ( face->GetType() != SMDSAbs_Face )
1320       continue;
1321
1322     const size_t nbCornersNodes = face->NbCornerNodes();
1323     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1324
1325     checkedVolumes.clear();
1326     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327     while ( vIt->more() )
1328     {
1329       const SMDS_MeshElement* volume = vIt->next();
1330
1331       if ( !checkedVolumes.insert( volume ).second )
1332         continue;
1333       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1334         continue;
1335
1336       // is volume adjacent?
1337       bool allNodesCommon = true;
1338       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340       if ( !allNodesCommon )
1341         continue;
1342
1343       // get nodes of a corresponding volume facet
1344       faceNodesSet.clear();
1345       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346       volumeTool.Set( volume );
1347       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348       if ( facetID < 0 ) continue;
1349       volumeTool.SetExternalNormal();
1350       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1351
1352       // compare order of faceNodes and facetNodes
1353       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1354       int iNN[2];
1355       for ( int i = 0; i < 2; ++i )
1356       {
1357         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359           if ( faceNodes[ iN ] == n )
1360           {
1361             iNN[ i ] = iN;
1362             break;
1363           }
1364       }
1365       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366       if ( isOutside != theOutsideNormal )
1367         nbReori += Reorient( face );
1368     }
1369   }  // loop on given faces
1370
1371   return nbReori;
1372 }
1373
1374 //=======================================================================
1375 //function : getBadRate
1376 //purpose  :
1377 //=======================================================================
1378
1379 static double getBadRate (const SMDS_MeshElement*               theElem,
1380                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1381 {
1382   SMESH::Controls::TSequenceOfXYZ P;
1383   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1384     return 1e100;
1385   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1387 }
1388
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose  : Cut quadrangles into triangles.
1392 //           theCrit is used to select a diagonal to cut
1393 //=======================================================================
1394
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1396                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1397 {
1398   ClearLastCreated();
1399
1400   if ( !theCrit.get() )
1401     return false;
1402
1403   SMESHDS_Mesh *       aMesh = GetMeshDS();
1404   Handle(Geom_Surface) surface;
1405   SMESH_MesherHelper   helper( *GetMesh() );
1406
1407   myLastCreatedElems.reserve( theElems.size() * 2 );
1408
1409   TIDSortedElemSet::iterator itElem;
1410   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1411   {
1412     const SMDS_MeshElement* elem = *itElem;
1413     if ( !elem || elem->GetType() != SMDSAbs_Face )
1414       continue;
1415     if ( elem->NbCornerNodes() != 4 )
1416       continue;
1417
1418     // retrieve element nodes
1419     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1420
1421     // compare two sets of possible triangles
1422     double aBadRate1, aBadRate2; // to what extent a set is bad
1423     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1426
1427     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1430
1431     const int aShapeId = FindShape( elem );
1432     const SMDS_MeshElement* newElem1 = 0;
1433     const SMDS_MeshElement* newElem2 = 0;
1434
1435     if ( !elem->IsQuadratic() ) // split linear quadrangle
1436     {
1437       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439       if ( aBadRate1 <= aBadRate2 ) {
1440         // tr1 + tr2 is better
1441         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1443       }
1444       else {
1445         // tr3 + tr4 is better
1446         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1448       }
1449     }
1450     else // split quadratic quadrangle
1451     {
1452       helper.SetIsQuadratic( true );
1453       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1454
1455       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456       if ( aNodes.size() == 9 )
1457       {
1458         helper.SetIsBiQuadratic( true );
1459         if ( aBadRate1 <= aBadRate2 )
1460           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1461         else
1462           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1463       }
1464       // create a new element
1465       if ( aBadRate1 <= aBadRate2 ) {
1466         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1468       }
1469       else {
1470         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1472       }
1473     } // quadratic case
1474
1475     // care of a new element
1476
1477     myLastCreatedElems.push_back(newElem1);
1478     myLastCreatedElems.push_back(newElem2);
1479     AddToSameGroups( newElem1, elem, aMesh );
1480     AddToSameGroups( newElem2, elem, aMesh );
1481
1482     // put a new triangle on the same shape
1483     if ( aShapeId )
1484       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1486
1487     aMesh->RemoveElement( elem );
1488   }
1489   return true;
1490 }
1491
1492 //=======================================================================
1493 /*!
1494  * \brief Split each of given quadrangles into 4 triangles.
1495  * \param theElems - The faces to be split. If empty all faces are split.
1496  */
1497 //=======================================================================
1498
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1500 {
1501   ClearLastCreated();
1502   myLastCreatedElems.reserve( theElems.size() * 4 );
1503
1504   SMESH_MesherHelper helper( *GetMesh() );
1505   helper.SetElementsOnShape( true );
1506
1507   SMDS_ElemIteratorPtr faceIt;
1508   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1509   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1510
1511   bool   checkUV;
1512   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1513   gp_XYZ xyz[9];
1514   vector< const SMDS_MeshNode* > nodes;
1515   SMESHDS_SubMesh*               subMeshDS = 0;
1516   TopoDS_Face                    F;
1517   Handle(Geom_Surface)           surface;
1518   TopLoc_Location                loc;
1519
1520   while ( faceIt->more() )
1521   {
1522     const SMDS_MeshElement* quad = faceIt->next();
1523     if ( !quad || quad->NbCornerNodes() != 4 )
1524       continue;
1525
1526     // get a surface the quad is on
1527
1528     if ( quad->getshapeId() < 1 )
1529     {
1530       F.Nullify();
1531       helper.SetSubShape( 0 );
1532       subMeshDS = 0;
1533     }
1534     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1535     {
1536       helper.SetSubShape( quad->getshapeId() );
1537       if ( !helper.GetSubShape().IsNull() &&
1538            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1539       {
1540         F = TopoDS::Face( helper.GetSubShape() );
1541         surface = BRep_Tool::Surface( F, loc );
1542         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1543       }
1544       else
1545       {
1546         helper.SetSubShape( 0 );
1547         subMeshDS = 0;
1548       }
1549     }
1550
1551     // create a central node
1552
1553     const SMDS_MeshNode* nCentral;
1554     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1555
1556     if ( nodes.size() == 9 )
1557     {
1558       nCentral = nodes.back();
1559     }
1560     else
1561     {
1562       size_t iN = 0;
1563       if ( F.IsNull() )
1564       {
1565         for ( ; iN < nodes.size(); ++iN )
1566           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1567
1568         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1569           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1570
1571         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572                                    xyz[0], xyz[1], xyz[2], xyz[3],
1573                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1574       }
1575       else
1576       {
1577         for ( ; iN < nodes.size(); ++iN )
1578           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1579
1580         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1581           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1582
1583         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584                                   uv[0], uv[1], uv[2], uv[3],
1585                                   uv[4], uv[5], uv[6], uv[7] );
1586
1587         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1588         xyz[ 8 ] = p.XYZ();
1589       }
1590
1591       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1592                                  uv[8].X(), uv[8].Y() );
1593       myLastCreatedNodes.push_back( nCentral );
1594     }
1595
1596     // create 4 triangles
1597
1598     helper.SetIsQuadratic  ( nodes.size() > 4 );
1599     helper.SetIsBiQuadratic( nodes.size() == 9 );
1600     if ( helper.GetIsQuadratic() )
1601       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1602
1603     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1604
1605     for ( int i = 0; i < 4; ++i )
1606     {
1607       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1608                                                nodes[(i+1)%4],
1609                                                nCentral );
1610       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1611       myLastCreatedElems.push_back( tria );
1612     }
1613   }
1614 }
1615
1616 //=======================================================================
1617 //function : BestSplit
1618 //purpose  : Find better diagonal for cutting.
1619 //=======================================================================
1620
1621 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1622                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1623 {
1624   ClearLastCreated();
1625
1626   if (!theCrit.get())
1627     return -1;
1628
1629   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1630     return -1;
1631
1632   if( theQuad->NbNodes()==4 ||
1633       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1634
1635     // retrieve element nodes
1636     const SMDS_MeshNode* aNodes [4];
1637     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1638     int i = 0;
1639     //while (itN->more())
1640     while (i<4) {
1641       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1642     }
1643     // compare two sets of possible triangles
1644     double aBadRate1, aBadRate2; // to what extent a set is bad
1645     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1646     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1647     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1648
1649     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1650     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1651     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1652     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1653     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1654     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1655       return 1; // diagonal 1-3
1656
1657     return 2; // diagonal 2-4
1658   }
1659   return -1;
1660 }
1661
1662 namespace
1663 {
1664   // Methods of splitting volumes into tetra
1665
1666   const int theHexTo5_1[5*4+1] =
1667     {
1668       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1669     };
1670   const int theHexTo5_2[5*4+1] =
1671     {
1672       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1673     };
1674   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1675
1676   const int theHexTo6_1[6*4+1] =
1677     {
1678       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
1679     };
1680   const int theHexTo6_2[6*4+1] =
1681     {
1682       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
1683     };
1684   const int theHexTo6_3[6*4+1] =
1685     {
1686       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
1687     };
1688   const int theHexTo6_4[6*4+1] =
1689     {
1690       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
1691     };
1692   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1693
1694   const int thePyraTo2_1[2*4+1] =
1695     {
1696       0, 1, 2, 4,    0, 2, 3, 4,   -1
1697     };
1698   const int thePyraTo2_2[2*4+1] =
1699     {
1700       1, 2, 3, 4,    1, 3, 0, 4,   -1
1701     };
1702   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1703
1704   const int thePentaTo3_1[3*4+1] =
1705     {
1706       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1707     };
1708   const int thePentaTo3_2[3*4+1] =
1709     {
1710       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1711     };
1712   const int thePentaTo3_3[3*4+1] =
1713     {
1714       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1715     };
1716   const int thePentaTo3_4[3*4+1] =
1717     {
1718       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1719     };
1720   const int thePentaTo3_5[3*4+1] =
1721     {
1722       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1723     };
1724   const int thePentaTo3_6[3*4+1] =
1725     {
1726       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1727     };
1728   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1729                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1730
1731   // Methods of splitting hexahedron into prisms
1732
1733   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1734     {
1735       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
1736     };
1737   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1738     {
1739       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
1740     };
1741   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1742     {
1743       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
1744     };
1745
1746   const int theHexTo2Prisms_BT_1[6*2+1] =
1747     {
1748       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1749     };
1750   const int theHexTo2Prisms_BT_2[6*2+1] =
1751     {
1752       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1753     };
1754   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1755
1756   const int theHexTo2Prisms_LR_1[6*2+1] =
1757     {
1758       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1759     };
1760   const int theHexTo2Prisms_LR_2[6*2+1] =
1761     {
1762       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1763     };
1764   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1765
1766   const int theHexTo2Prisms_FB_1[6*2+1] =
1767     {
1768       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1769     };
1770   const int theHexTo2Prisms_FB_2[6*2+1] =
1771     {
1772       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1773     };
1774   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1775
1776
1777   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1778   {
1779     int _n1, _n2, _n3;
1780     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1781     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1782     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1783                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1784   };
1785   struct TSplitMethod
1786   {
1787     int        _nbSplits;
1788     int        _nbCorners;
1789     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1790     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1791     bool       _ownConn;      //!< to delete _connectivity in destructor
1792     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1793
1794     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1795       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1796     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1797     bool hasFacet( const TTriangleFacet& facet ) const
1798     {
1799       if ( _nbCorners == 4 )
1800       {
1801         const int* tetConn = _connectivity;
1802         for ( ; tetConn[0] >= 0; tetConn += 4 )
1803           if (( facet.contains( tetConn[0] ) +
1804                 facet.contains( tetConn[1] ) +
1805                 facet.contains( tetConn[2] ) +
1806                 facet.contains( tetConn[3] )) == 3 )
1807             return true;
1808       }
1809       else // prism, _nbCorners == 6
1810       {
1811         const int* prismConn = _connectivity;
1812         for ( ; prismConn[0] >= 0; prismConn += 6 )
1813         {
1814           if (( facet.contains( prismConn[0] ) &&
1815                 facet.contains( prismConn[1] ) &&
1816                 facet.contains( prismConn[2] ))
1817               ||
1818               ( facet.contains( prismConn[3] ) &&
1819                 facet.contains( prismConn[4] ) &&
1820                 facet.contains( prismConn[5] )))
1821             return true;
1822         }
1823       }
1824       return false;
1825     }
1826   };
1827
1828   //=======================================================================
1829   /*!
1830    * \brief return TSplitMethod for the given element to split into tetrahedra
1831    */
1832   //=======================================================================
1833
1834   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1835   {
1836     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1837
1838     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1839     // an edge and a face barycenter; tertaherdons are based on triangles and
1840     // a volume barycenter
1841     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1842
1843     // Find out how adjacent volumes are split
1844
1845     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1846     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1847     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1848     {
1849       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1850       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1851       if ( nbNodes < 4 ) continue;
1852
1853       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1854       const int* nInd = vol.GetFaceNodesIndices( iF );
1855       if ( nbNodes == 4 )
1856       {
1857         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1858         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1859         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1860         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1861       }
1862       else
1863       {
1864         int iCom = 0; // common node of triangle faces to split into
1865         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1866         {
1867           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1868                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1869                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1870           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1871                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1872                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1873           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1874           {
1875             triaSplits.push_back( t012 );
1876             triaSplits.push_back( t023 );
1877             break;
1878           }
1879         }
1880       }
1881       if ( !triaSplits.empty() )
1882         hasAdjacentSplits = true;
1883     }
1884
1885     // Among variants of split method select one compliant with adjacent volumes
1886
1887     TSplitMethod method;
1888     if ( !vol.Element()->IsPoly() && !is24TetMode )
1889     {
1890       int nbVariants = 2, nbTet = 0;
1891       const int** connVariants = 0;
1892       switch ( vol.Element()->GetEntityType() )
1893       {
1894       case SMDSEntity_Hexa:
1895       case SMDSEntity_Quad_Hexa:
1896       case SMDSEntity_TriQuad_Hexa:
1897         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1898           connVariants = theHexTo5, nbTet = 5;
1899         else
1900           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1901         break;
1902       case SMDSEntity_Pyramid:
1903       case SMDSEntity_Quad_Pyramid:
1904         connVariants = thePyraTo2;  nbTet = 2;
1905         break;
1906       case SMDSEntity_Penta:
1907       case SMDSEntity_Quad_Penta:
1908       case SMDSEntity_BiQuad_Penta:
1909         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1910         break;
1911       default:
1912         nbVariants = 0;
1913       }
1914       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1915       {
1916         // check method compliancy with adjacent tetras,
1917         // all found splits must be among facets of tetras described by this method
1918         method = TSplitMethod( nbTet, connVariants[variant] );
1919         if ( hasAdjacentSplits && method._nbSplits > 0 )
1920         {
1921           bool facetCreated = true;
1922           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1923           {
1924             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1925             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1926               facetCreated = method.hasFacet( *facet );
1927           }
1928           if ( !facetCreated )
1929             method = TSplitMethod(0); // incompatible method
1930         }
1931       }
1932     }
1933     if ( method._nbSplits < 1 )
1934     {
1935       // No standard method is applicable, use a generic solution:
1936       // each facet of a volume is split into triangles and
1937       // each of triangles and a volume barycenter form a tetrahedron.
1938
1939       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1940
1941       int* connectivity = new int[ maxTetConnSize + 1 ];
1942       method._connectivity = connectivity;
1943       method._ownConn = true;
1944       method._baryNode = !isHex27; // to create central node or not
1945
1946       int connSize = 0;
1947       int baryCenInd = vol.NbNodes() - int( isHex27 );
1948       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1949       {
1950         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1951         const int*   nInd = vol.GetFaceNodesIndices( iF );
1952         // find common node of triangle facets of tetra to create
1953         int iCommon = 0; // index in linear numeration
1954         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1955         if ( !triaSplits.empty() )
1956         {
1957           // by found facets
1958           const TTriangleFacet* facet = &triaSplits.front();
1959           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1960             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1961                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1962               break;
1963         }
1964         else if ( nbNodes > 3 && !is24TetMode )
1965         {
1966           // find the best method of splitting into triangles by aspect ratio
1967           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1968           map< double, int > badness2iCommon;
1969           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1970           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1971           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1972           {
1973             double badness = 0;
1974             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1975             {
1976               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1977                                       nodes[ iQ*((iLast-1)%nbNodes)],
1978                                       nodes[ iQ*((iLast  )%nbNodes)]);
1979               badness += getBadRate( &tria, aspectRatio );
1980             }
1981             badness2iCommon.insert( make_pair( badness, iCommon ));
1982           }
1983           // use iCommon with lowest badness
1984           iCommon = badness2iCommon.begin()->second;
1985         }
1986         if ( iCommon >= nbNodes )
1987           iCommon = 0; // something wrong
1988
1989         // fill connectivity of tetrahedra based on a current face
1990         int nbTet = nbNodes - 2;
1991         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1992         {
1993           int faceBaryCenInd;
1994           if ( isHex27 )
1995           {
1996             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1997             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1998           }
1999           else
2000           {
2001             method._faceBaryNode[ iF ] = 0;
2002             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2003           }
2004           nbTet = nbNodes;
2005           for ( int i = 0; i < nbTet; ++i )
2006           {
2007             int i1 = i, i2 = (i+1) % nbNodes;
2008             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2010             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2011             connectivity[ connSize++ ] = faceBaryCenInd;
2012             connectivity[ connSize++ ] = baryCenInd;
2013           }
2014         }
2015         else
2016         {
2017           for ( int i = 0; i < nbTet; ++i )
2018           {
2019             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2020             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2022             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2023             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2024             connectivity[ connSize++ ] = baryCenInd;
2025           }
2026         }
2027         method._nbSplits += nbTet;
2028
2029       } // loop on volume faces
2030
2031       connectivity[ connSize++ ] = -1;
2032
2033     } // end of generic solution
2034
2035     return method;
2036   }
2037   //=======================================================================
2038   /*!
2039    * \brief return TSplitMethod to split haxhedron into prisms
2040    */
2041   //=======================================================================
2042
2043   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2044                                     const int        methodFlags,
2045                                     const int        facetToSplit)
2046   {
2047     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2048     // B, T, L, B, R, F
2049     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2050
2051     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2052     {
2053       static TSplitMethod to4methods[4]; // order BT, LR, FB
2054       if ( to4methods[iF]._nbSplits == 0 )
2055       {
2056         switch ( iF ) {
2057         case 0:
2058           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2059           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2060           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2061           break;
2062         case 1:
2063           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2064           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2065           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2066           break;
2067         case 2:
2068           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2069           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2070           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2071           break;
2072         default: return to4methods[3];
2073         }
2074         to4methods[iF]._nbSplits  = 4;
2075         to4methods[iF]._nbCorners = 6;
2076       }
2077       return to4methods[iF];
2078     }
2079     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2080
2081     TSplitMethod method;
2082
2083     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2084
2085     const int nbVariants = 2, nbSplits = 2;
2086     const int** connVariants = 0;
2087     switch ( iF ) {
2088     case 0: connVariants = theHexTo2Prisms_BT; break;
2089     case 1: connVariants = theHexTo2Prisms_LR; break;
2090     case 2: connVariants = theHexTo2Prisms_FB; break;
2091     default: return method;
2092     }
2093
2094     // look for prisms adjacent via facetToSplit and an opposite one
2095     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2096     {
2097       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2098       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2099       if ( nbNodes != 4 ) return method;
2100
2101       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2102       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2103       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2104       TTriangleFacet* t;
2105       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2106         t = &t012;
2107       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2108         t = &t123;
2109       else
2110         continue;
2111
2112       // there are adjacent prism
2113       for ( int variant = 0; variant < nbVariants; ++variant )
2114       {
2115         // check method compliancy with adjacent prisms,
2116         // the found prism facets must be among facets of prisms described by current method
2117         method._nbSplits     = nbSplits;
2118         method._nbCorners    = 6;
2119         method._connectivity = connVariants[ variant ];
2120         if ( method.hasFacet( *t ))
2121           return method;
2122       }
2123     }
2124
2125     // No adjacent prisms. Select a variant with a best aspect ratio.
2126
2127     double badness[2] = { 0., 0. };
2128     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2129     const SMDS_MeshNode** nodes = vol.GetNodes();
2130     for ( int variant = 0; variant < nbVariants; ++variant )
2131       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2132       {
2133         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2134         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2135
2136         method._connectivity = connVariants[ variant ];
2137         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2138         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2139         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2140
2141         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2142                                 nodes[ t->_n2 ],
2143                                 nodes[ t->_n3 ] );
2144         badness[ variant ] += getBadRate( &tria, aspectRatio );
2145       }
2146     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2147
2148     method._nbSplits     = nbSplits;
2149     method._nbCorners    = 6;
2150     method._connectivity = connVariants[ iBetter ];
2151
2152     return method;
2153   }
2154
2155   //================================================================================
2156   /*!
2157    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2158    */
2159   //================================================================================
2160
2161   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2162                                        const SMDSAbs_GeometryType geom ) const
2163   {
2164     // find the tetrahedron including the three nodes of facet
2165     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2166     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2167     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2168     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2169     while ( volIt1->more() )
2170     {
2171       const SMDS_MeshElement* v = volIt1->next();
2172       if ( v->GetGeomType() != geom )
2173         continue;
2174       const int lastCornerInd = v->NbCornerNodes() - 1;
2175       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2176         continue; // medium node not allowed
2177       const int ind2 = v->GetNodeIndex( n2 );
2178       if ( ind2 < 0 || lastCornerInd < ind2 )
2179         continue;
2180       const int ind3 = v->GetNodeIndex( n3 );
2181       if ( ind3 < 0 || lastCornerInd < ind3 )
2182         continue;
2183       return true;
2184     }
2185     return false;
2186   }
2187
2188   //=======================================================================
2189   /*!
2190    * \brief A key of a face of volume
2191    */
2192   //=======================================================================
2193
2194   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2195   {
2196     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2197     {
2198       TIDSortedNodeSet sortedNodes;
2199       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2200       int nbNodes = vol.NbFaceNodes( iF );
2201       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2202       for ( int i = 0; i < nbNodes; i += iQ )
2203         sortedNodes.insert( fNodes[i] );
2204       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2205       first.first   = (*(n++))->GetID();
2206       first.second  = (*(n++))->GetID();
2207       second.first  = (*(n++))->GetID();
2208       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2209     }
2210   };
2211 } // namespace
2212
2213 //=======================================================================
2214 //function : SplitVolumes
2215 //purpose  : Split volume elements into tetrahedra or prisms.
2216 //           If facet ID < 0, element is split into tetrahedra,
2217 //           else a hexahedron is split into prisms so that the given facet is
2218 //           split into triangles
2219 //=======================================================================
2220
2221 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2222                                      const int            theMethodFlags)
2223 {
2224   SMDS_VolumeTool    volTool;
2225   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2226   fHelper.ToFixNodeParameters( true );
2227
2228   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2229   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2230
2231   SMESH_SequenceOfElemPtr newNodes, newElems;
2232
2233   // map face of volume to it's baricenrtic node
2234   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2235   double bc[3];
2236   vector<const SMDS_MeshElement* > splitVols;
2237
2238   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2239   for ( ; elem2facet != theElems.end(); ++elem2facet )
2240   {
2241     const SMDS_MeshElement* elem = elem2facet->first;
2242     const int       facetToSplit = elem2facet->second;
2243     if ( elem->GetType() != SMDSAbs_Volume )
2244       continue;
2245     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2246     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2247       continue;
2248
2249     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2250
2251     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2252                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2253                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2254     if ( splitMethod._nbSplits < 1 ) continue;
2255
2256     // find submesh to add new tetras to
2257     if ( !subMesh || !subMesh->Contains( elem ))
2258     {
2259       int shapeID = FindShape( elem );
2260       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2261       subMesh = GetMeshDS()->MeshElements( shapeID );
2262     }
2263     int iQ;
2264     if ( elem->IsQuadratic() )
2265     {
2266       iQ = 2;
2267       // add quadratic links to the helper
2268       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2269       {
2270         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2271         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2272         for ( int iN = 0; iN < nbN; iN += iQ )
2273           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2274       }
2275       helper.SetIsQuadratic( true );
2276     }
2277     else
2278     {
2279       iQ = 1;
2280       helper.SetIsQuadratic( false );
2281     }
2282     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2283                                         volTool.GetNodes() + elem->NbNodes() );
2284     helper.SetElementsOnShape( true );
2285     if ( splitMethod._baryNode )
2286     {
2287       // make a node at barycenter
2288       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2289       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2290       nodes.push_back( gcNode );
2291       newNodes.push_back( gcNode );
2292     }
2293     if ( !splitMethod._faceBaryNode.empty() )
2294     {
2295       // make or find baricentric nodes of faces
2296       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2297       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2298       {
2299         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2300           volFace2BaryNode.insert
2301           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2302         if ( !f_n->second )
2303         {
2304           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2305           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2306         }
2307         nodes.push_back( iF_n->second = f_n->second );
2308       }
2309     }
2310
2311     // make new volumes
2312     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2313     const int* volConn = splitMethod._connectivity;
2314     if ( splitMethod._nbCorners == 4 ) // tetra
2315       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2316         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2317                                                                nodes[ volConn[1] ],
2318                                                                nodes[ volConn[2] ],
2319                                                                nodes[ volConn[3] ]));
2320     else // prisms
2321       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323                                                                nodes[ volConn[1] ],
2324                                                                nodes[ volConn[2] ],
2325                                                                nodes[ volConn[3] ],
2326                                                                nodes[ volConn[4] ],
2327                                                                nodes[ volConn[5] ]));
2328
2329     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2330
2331     // Split faces on sides of the split volume
2332
2333     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2334     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2335     {
2336       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2337       if ( nbNodes < 4 ) continue;
2338
2339       // find an existing face
2340       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2341                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2342       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2343                                                                        /*noMedium=*/false))
2344       {
2345         // make triangles
2346         helper.SetElementsOnShape( false );
2347         vector< const SMDS_MeshElement* > triangles;
2348
2349         // find submesh to add new triangles in
2350         if ( !fSubMesh || !fSubMesh->Contains( face ))
2351         {
2352           int shapeID = FindShape( face );
2353           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2354         }
2355         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2356         if ( iF_n != splitMethod._faceBaryNode.end() )
2357         {
2358           const SMDS_MeshNode *baryNode = iF_n->second;
2359           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2360           {
2361             const SMDS_MeshNode* n1 = fNodes[iN];
2362             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2363             const SMDS_MeshNode *n3 = baryNode;
2364             if ( !volTool.IsFaceExternal( iF ))
2365               swap( n2, n3 );
2366             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2367           }
2368           if ( fSubMesh ) // update position of the bary node on geometry
2369           {
2370             if ( subMesh )
2371               subMesh->RemoveNode( baryNode );
2372             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2373             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2374             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2375             {
2376               fHelper.SetSubShape( s );
2377               gp_XY uv( 1e100, 1e100 );
2378               double distXYZ[4];
2379               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2380                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2381                    uv.X() < 1e100 )
2382               {
2383                 // node is too far from the surface
2384                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2385                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2386                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2387               }
2388             }
2389           }
2390         }
2391         else
2392         {
2393           // among possible triangles create ones described by split method
2394           const int* nInd = volTool.GetFaceNodesIndices( iF );
2395           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2396           int iCom = 0; // common node of triangle faces to split into
2397           list< TTriangleFacet > facets;
2398           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2399           {
2400             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2401                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2402                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2403             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2404                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2405                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2406             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2407             {
2408               facets.push_back( t012 );
2409               facets.push_back( t023 );
2410               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2411                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2412                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2413                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2414               break;
2415             }
2416           }
2417           list< TTriangleFacet >::iterator facet = facets.begin();
2418           if ( facet == facets.end() )
2419             break;
2420           for ( ; facet != facets.end(); ++facet )
2421           {
2422             if ( !volTool.IsFaceExternal( iF ))
2423               swap( facet->_n2, facet->_n3 );
2424             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2425                                                  volNodes[ facet->_n2 ],
2426                                                  volNodes[ facet->_n3 ]));
2427           }
2428         }
2429         for ( size_t i = 0; i < triangles.size(); ++i )
2430         {
2431           if ( !triangles[ i ]) continue;
2432           if ( fSubMesh )
2433             fSubMesh->AddElement( triangles[ i ]);
2434           newElems.push_back( triangles[ i ]);
2435         }
2436         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2437         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2438
2439       } // while a face based on facet nodes exists
2440     } // loop on volume faces to split them into triangles
2441
2442     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2443
2444     if ( geomType == SMDSEntity_TriQuad_Hexa )
2445     {
2446       // remove medium nodes that could become free
2447       for ( int i = 20; i < volTool.NbNodes(); ++i )
2448         if ( volNodes[i]->NbInverseElements() == 0 )
2449           GetMeshDS()->RemoveNode( volNodes[i] );
2450     }
2451   } // loop on volumes to split
2452
2453   myLastCreatedNodes = newNodes;
2454   myLastCreatedElems = newElems;
2455 }
2456
2457 //=======================================================================
2458 //function : GetHexaFacetsToSplit
2459 //purpose  : For hexahedra that will be split into prisms, finds facets to
2460 //           split into triangles. Only hexahedra adjacent to the one closest
2461 //           to theFacetNormal.Location() are returned.
2462 //param [in,out] theHexas - the hexahedra
2463 //param [in]     theFacetNormal - facet normal
2464 //param [out]    theFacets - the hexahedra and found facet IDs
2465 //=======================================================================
2466
2467 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2468                                              const gp_Ax1&     theFacetNormal,
2469                                              TFacetOfElem &    theFacets)
2470 {
2471 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2472
2473   // Find a hexa closest to the location of theFacetNormal
2474
2475   const SMDS_MeshElement* startHex;
2476   {
2477     // get SMDS_ElemIteratorPtr on theHexas
2478     typedef const SMDS_MeshElement*                                      TValue;
2479     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2480     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2481     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2482     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2483     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2484       ( new TElemSetIter( theHexas.begin(),
2485                           theHexas.end(),
2486                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2487
2488     SMESH_ElementSearcher* searcher =
2489       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2490
2491     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2492
2493     delete searcher;
2494
2495     if ( !startHex )
2496       throw SALOME_Exception( THIS_METHOD "startHex not found");
2497   }
2498
2499   // Select a facet of startHex by theFacetNormal
2500
2501   SMDS_VolumeTool vTool( startHex );
2502   double norm[3], dot, maxDot = 0;
2503   int facetID = -1;
2504   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2505     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2506     {
2507       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2508       if ( dot > maxDot )
2509       {
2510         facetID = iF;
2511         maxDot = dot;
2512       }
2513     }
2514   if ( facetID < 0 )
2515     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2516
2517   // Fill theFacets starting from facetID of startHex
2518
2519   // facets used for searching of volumes adjacent to already treated ones
2520   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2521   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2522   TFacetMap facetsToCheck;
2523
2524   set<const SMDS_MeshNode*> facetNodes;
2525   const SMDS_MeshElement*   curHex;
2526
2527   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2528
2529   while ( startHex )
2530   {
2531     // move in two directions from startHex via facetID
2532     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2533     {
2534       curHex       = startHex;
2535       int curFacet = facetID;
2536       if ( is2nd ) // do not treat startHex twice
2537       {
2538         vTool.Set( curHex );
2539         if ( vTool.IsFreeFace( curFacet, &curHex ))
2540         {
2541           curHex = 0;
2542         }
2543         else
2544         {
2545           vTool.GetFaceNodes( curFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes );
2548         }
2549       }
2550       while ( curHex )
2551       {
2552         // store a facet to split
2553         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2554         {
2555           theFacets.insert( make_pair( curHex, -1 ));
2556           break;
2557         }
2558         if ( !allHex && !theHexas.count( curHex ))
2559           break;
2560
2561         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2562           theFacets.insert( make_pair( curHex, curFacet ));
2563         if ( !facetIt2isNew.second )
2564           break;
2565
2566         // remember not-to-split facets in facetsToCheck
2567         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2568         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2569         {
2570           if ( iF == curFacet && iF == oppFacet )
2571             continue;
2572           TVolumeFaceKey facetKey ( vTool, iF );
2573           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2574           pair< TFacetMap::iterator, bool > it2isnew =
2575             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2576           if ( !it2isnew.second )
2577             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2578         }
2579         // pass to a volume adjacent via oppFacet
2580         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2581         {
2582           curHex = 0;
2583         }
2584         else
2585         {
2586           // get a new curFacet
2587           vTool.GetFaceNodes( oppFacet, facetNodes );
2588           vTool.Set( curHex );
2589           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2590         }
2591       }
2592     } // move in two directions from startHex via facetID
2593
2594     // Find a new startHex by facetsToCheck
2595
2596     startHex = 0;
2597     facetID  = -1;
2598     TFacetMap::iterator fIt = facetsToCheck.begin();
2599     while ( !startHex && fIt != facetsToCheck.end() )
2600     {
2601       const TElemFacets&  elemFacets = fIt->second;
2602       const SMDS_MeshElement*    hex = elemFacets.first->first;
2603       int                 splitFacet = elemFacets.first->second;
2604       int               lateralFacet = elemFacets.second;
2605       facetsToCheck.erase( fIt );
2606       fIt = facetsToCheck.begin();
2607
2608       vTool.Set( hex );
2609       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2610            curHex->GetGeomType() != SMDSGeom_HEXA )
2611         continue;
2612       if ( !allHex && !theHexas.count( curHex ))
2613         continue;
2614
2615       startHex = curHex;
2616
2617       // find a facet of startHex to split
2618
2619       set<const SMDS_MeshNode*> lateralNodes;
2620       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2621       vTool.GetFaceNodes( splitFacet,   facetNodes );
2622       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2623       vTool.Set( startHex );
2624       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2625
2626       // look for a facet of startHex having common nodes with facetNodes
2627       // but not lateralFacet
2628       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2629       {
2630         if ( iF == lateralFacet )
2631           continue;
2632         int nbCommonNodes = 0;
2633         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2634         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2635           nbCommonNodes += facetNodes.count( nn[ iN ]);
2636
2637         if ( nbCommonNodes >= 2 )
2638         {
2639           facetID = iF;
2640           break;
2641         }
2642       }
2643       if ( facetID < 0 )
2644         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2645     }
2646   } //   while ( startHex )
2647
2648   return;
2649 }
2650
2651 namespace
2652 {
2653   //================================================================================
2654   /*!
2655    * \brief Selects nodes of several elements according to a given interlace
2656    *  \param [in] srcNodes - nodes to select from
2657    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2658    *  \param [in] interlace - indices of nodes for all elements
2659    *  \param [in] nbElems - nb of elements
2660    *  \param [in] nbNodes - nb of nodes in each element
2661    *  \param [in] mesh - the mesh
2662    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2663    *  \param [in] type - type of elements to look for
2664    */
2665   //================================================================================
2666
2667   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2668                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2669                     const int*                            interlace,
2670                     const int                             nbElems,
2671                     const int                             nbNodes,
2672                     SMESHDS_Mesh*                         mesh = 0,
2673                     list< const SMDS_MeshElement* >*      elemQueue=0,
2674                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2675   {
2676     for ( int iE = 0; iE < nbElems; ++iE )
2677     {
2678       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2679       const int*                         select = & interlace[iE*nbNodes];
2680       elemNodes.resize( nbNodes );
2681       for ( int iN = 0; iN < nbNodes; ++iN )
2682         elemNodes[iN] = srcNodes[ select[ iN ]];
2683     }
2684     const SMDS_MeshElement* e;
2685     if ( elemQueue )
2686       for ( int iE = 0; iE < nbElems; ++iE )
2687         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2688           elemQueue->push_back( e );
2689   }
2690 }
2691
2692 //=======================================================================
2693 /*
2694  * Split bi-quadratic elements into linear ones without creation of additional nodes
2695  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2696  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2697  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2698  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2699  *   will be split in order to keep the mesh conformal.
2700  *  \param elems - elements to split
2701  */
2702 //=======================================================================
2703
2704 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2705 {
2706   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2707   vector<const SMDS_MeshElement* > splitElems;
2708   list< const SMDS_MeshElement* > elemQueue;
2709   list< const SMDS_MeshElement* >::iterator elemIt;
2710
2711   SMESHDS_Mesh * mesh = GetMeshDS();
2712   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2713   int nbElems, nbNodes;
2714
2715   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2716   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2717   {
2718     elemQueue.clear();
2719     elemQueue.push_back( *elemSetIt );
2720     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2721     {
2722       const SMDS_MeshElement* elem = *elemIt;
2723       switch( elem->GetEntityType() )
2724       {
2725       case SMDSEntity_TriQuad_Hexa: // HEX27
2726       {
2727         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2728         nbElems  = nbNodes = 8;
2729         elemType = & hexaType;
2730
2731         // get nodes for new elements
2732         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2733                                  { 1,9,20,8,    17,22,26,21 },
2734                                  { 2,10,20,9,   18,23,26,22 },
2735                                  { 3,11,20,10,  19,24,26,23 },
2736                                  { 16,21,26,24, 4,12,25,15  },
2737                                  { 17,22,26,21, 5,13,25,12  },
2738                                  { 18,23,26,22, 6,14,25,13  },
2739                                  { 19,24,26,23, 7,15,25,14  }};
2740         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2741
2742         // add boundary faces to elemQueue
2743         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2744                                  { 4,5,6,7, 12,13,14,15, 25 },
2745                                  { 0,1,5,4, 8,17,12,16,  21 },
2746                                  { 1,2,6,5, 9,18,13,17,  22 },
2747                                  { 2,3,7,6, 10,19,14,18, 23 },
2748                                  { 3,0,4,7, 11,16,15,19, 24 }};
2749         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2750
2751         // add boundary segments to elemQueue
2752         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2753                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2754                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2755         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2756         break;
2757       }
2758       case SMDSEntity_BiQuad_Triangle: // TRIA7
2759       {
2760         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2761         nbElems = 3;
2762         nbNodes = 4;
2763         elemType = & quadType;
2764
2765         // get nodes for new elements
2766         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2767         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2768
2769         // add boundary segments to elemQueue
2770         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2771         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2772         break;
2773       }
2774       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2775       {
2776         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2777         nbElems = 4;
2778         nbNodes = 4;
2779         elemType = & quadType;
2780
2781         // get nodes for new elements
2782         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2783         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2784
2785         // add boundary segments to elemQueue
2786         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2787         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2788         break;
2789       }
2790       case SMDSEntity_Quad_Edge:
2791       {
2792         if ( elemIt == elemQueue.begin() )
2793           continue; // an elem is in theElems
2794         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2795         nbElems = 2;
2796         nbNodes = 2;
2797         elemType = & segType;
2798
2799         // get nodes for new elements
2800         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2801         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2802         break;
2803       }
2804       default: continue;
2805       } // switch( elem->GetEntityType() )
2806
2807       // Create new elements
2808
2809       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2810
2811       splitElems.clear();
2812
2813       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2814       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2815       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2816       //elemType->SetID( -1 );
2817
2818       for ( int iE = 0; iE < nbElems; ++iE )
2819         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2820
2821
2822       ReplaceElemInGroups( elem, splitElems, mesh );
2823
2824       if ( subMesh )
2825         for ( size_t i = 0; i < splitElems.size(); ++i )
2826           subMesh->AddElement( splitElems[i] );
2827     }
2828   }
2829 }
2830
2831 //=======================================================================
2832 //function : AddToSameGroups
2833 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2834 //=======================================================================
2835
2836 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2837                                         const SMDS_MeshElement* elemInGroups,
2838                                         SMESHDS_Mesh *          aMesh)
2839 {
2840   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2841   if (!groups.empty()) {
2842     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2843     for ( ; grIt != groups.end(); grIt++ ) {
2844       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2845       if ( group && group->Contains( elemInGroups ))
2846         group->SMDSGroup().Add( elemToAdd );
2847     }
2848   }
2849 }
2850
2851
2852 //=======================================================================
2853 //function : RemoveElemFromGroups
2854 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2855 //=======================================================================
2856 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2857                                              SMESHDS_Mesh *          aMesh)
2858 {
2859   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2860   if (!groups.empty())
2861   {
2862     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2863     for (; GrIt != groups.end(); GrIt++)
2864     {
2865       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2866       if (!grp || grp->IsEmpty()) continue;
2867       grp->SMDSGroup().Remove(removeelem);
2868     }
2869   }
2870 }
2871
2872 //================================================================================
2873 /*!
2874  * \brief Replace elemToRm by elemToAdd in the all groups
2875  */
2876 //================================================================================
2877
2878 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2879                                             const SMDS_MeshElement* elemToAdd,
2880                                             SMESHDS_Mesh *          aMesh)
2881 {
2882   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2883   if (!groups.empty()) {
2884     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2885     for ( ; grIt != groups.end(); grIt++ ) {
2886       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2887       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2888         group->SMDSGroup().Add( elemToAdd );
2889     }
2890   }
2891 }
2892
2893 //================================================================================
2894 /*!
2895  * \brief Replace elemToRm by elemToAdd in the all groups
2896  */
2897 //================================================================================
2898
2899 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2900                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2901                                             SMESHDS_Mesh *                         aMesh)
2902 {
2903   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2904   if (!groups.empty())
2905   {
2906     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2907     for ( ; grIt != groups.end(); grIt++ ) {
2908       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2909       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2910         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2911           group->SMDSGroup().Add( elemToAdd[ i ] );
2912     }
2913   }
2914 }
2915
2916 //=======================================================================
2917 //function : QuadToTri
2918 //purpose  : Cut quadrangles into triangles.
2919 //           theCrit is used to select a diagonal to cut
2920 //=======================================================================
2921
2922 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2923                                   const bool         the13Diag)
2924 {
2925   ClearLastCreated();
2926   myLastCreatedElems.reserve( theElems.size() * 2 );
2927
2928   SMESHDS_Mesh *       aMesh = GetMeshDS();
2929   Handle(Geom_Surface) surface;
2930   SMESH_MesherHelper   helper( *GetMesh() );
2931
2932   TIDSortedElemSet::iterator itElem;
2933   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2934   {
2935     const SMDS_MeshElement* elem = *itElem;
2936     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2937       continue;
2938
2939     if ( elem->NbNodes() == 4 ) {
2940       // retrieve element nodes
2941       const SMDS_MeshNode* aNodes [4];
2942       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2943       int i = 0;
2944       while ( itN->more() )
2945         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2946
2947       int aShapeId = FindShape( elem );
2948       const SMDS_MeshElement* newElem1 = 0;
2949       const SMDS_MeshElement* newElem2 = 0;
2950       if ( the13Diag ) {
2951         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2952         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2953       }
2954       else {
2955         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2956         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2957       }
2958       myLastCreatedElems.push_back(newElem1);
2959       myLastCreatedElems.push_back(newElem2);
2960       // put a new triangle on the same shape and add to the same groups
2961       if ( aShapeId )
2962       {
2963         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2964         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2965       }
2966       AddToSameGroups( newElem1, elem, aMesh );
2967       AddToSameGroups( newElem2, elem, aMesh );
2968       aMesh->RemoveElement( elem );
2969     }
2970
2971     // Quadratic quadrangle
2972
2973     else if ( elem->NbNodes() >= 8 )
2974     {
2975       // get surface elem is on
2976       int aShapeId = FindShape( elem );
2977       if ( aShapeId != helper.GetSubShapeID() ) {
2978         surface.Nullify();
2979         TopoDS_Shape shape;
2980         if ( aShapeId > 0 )
2981           shape = aMesh->IndexToShape( aShapeId );
2982         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2983           TopoDS_Face face = TopoDS::Face( shape );
2984           surface = BRep_Tool::Surface( face );
2985           if ( !surface.IsNull() )
2986             helper.SetSubShape( shape );
2987         }
2988       }
2989
2990       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2991       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2992       for ( int i = 0; itN->more(); ++i )
2993         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2994
2995       const SMDS_MeshNode* centrNode = aNodes[8];
2996       if ( centrNode == 0 )
2997       {
2998         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2999                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3000                                            surface.IsNull() );
3001         myLastCreatedNodes.push_back(centrNode);
3002       }
3003
3004       // create a new element
3005       const SMDS_MeshElement* newElem1 = 0;
3006       const SMDS_MeshElement* newElem2 = 0;
3007       if ( the13Diag ) {
3008         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3009                                   aNodes[6], aNodes[7], centrNode );
3010         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3011                                   centrNode, aNodes[4], aNodes[5] );
3012       }
3013       else {
3014         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3015                                   aNodes[7], aNodes[4], centrNode );
3016         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3017                                   centrNode, aNodes[5], aNodes[6] );
3018       }
3019       myLastCreatedElems.push_back(newElem1);
3020       myLastCreatedElems.push_back(newElem2);
3021       // put a new triangle on the same shape and add to the same groups
3022       if ( aShapeId )
3023       {
3024         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3025         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3026       }
3027       AddToSameGroups( newElem1, elem, aMesh );
3028       AddToSameGroups( newElem2, elem, aMesh );
3029       aMesh->RemoveElement( elem );
3030     }
3031   }
3032
3033   return true;
3034 }
3035
3036 //=======================================================================
3037 //function : getAngle
3038 //purpose  :
3039 //=======================================================================
3040
3041 double getAngle(const SMDS_MeshElement * tr1,
3042                 const SMDS_MeshElement * tr2,
3043                 const SMDS_MeshNode *    n1,
3044                 const SMDS_MeshNode *    n2)
3045 {
3046   double angle = 2. * M_PI; // bad angle
3047
3048   // get normals
3049   SMESH::Controls::TSequenceOfXYZ P1, P2;
3050   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3051        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3052     return angle;
3053   gp_Vec N1,N2;
3054   if(!tr1->IsQuadratic())
3055     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3056   else
3057     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3058   if ( N1.SquareMagnitude() <= gp::Resolution() )
3059     return angle;
3060   if(!tr2->IsQuadratic())
3061     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3062   else
3063     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3064   if ( N2.SquareMagnitude() <= gp::Resolution() )
3065     return angle;
3066
3067   // find the first diagonal node n1 in the triangles:
3068   // take in account a diagonal link orientation
3069   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3070   for ( int t = 0; t < 2; t++ ) {
3071     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3072     int i = 0, iDiag = -1;
3073     while ( it->more()) {
3074       const SMDS_MeshElement *n = it->next();
3075       if ( n == n1 || n == n2 ) {
3076         if ( iDiag < 0)
3077           iDiag = i;
3078         else {
3079           if ( i - iDiag == 1 )
3080             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3081           else
3082             nFirst[ t ] = n;
3083           break;
3084         }
3085       }
3086       i++;
3087     }
3088   }
3089   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3090     N2.Reverse();
3091
3092   angle = N1.Angle( N2 );
3093   //SCRUTE( angle );
3094   return angle;
3095 }
3096
3097 // =================================================
3098 // class generating a unique ID for a pair of nodes
3099 // and able to return nodes by that ID
3100 // =================================================
3101 class LinkID_Gen {
3102 public:
3103
3104   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3105     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3106   {}
3107
3108   long GetLinkID (const SMDS_MeshNode * n1,
3109                   const SMDS_MeshNode * n2) const
3110   {
3111     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3112   }
3113
3114   bool GetNodes (const long             theLinkID,
3115                  const SMDS_MeshNode* & theNode1,
3116                  const SMDS_MeshNode* & theNode2) const
3117   {
3118     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3119     if ( !theNode1 ) return false;
3120     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3121     if ( !theNode2 ) return false;
3122     return true;
3123   }
3124
3125 private:
3126   LinkID_Gen();
3127   const SMESHDS_Mesh* myMesh;
3128   long                myMaxID;
3129 };
3130
3131
3132 //=======================================================================
3133 //function : TriToQuad
3134 //purpose  : Fuse neighbour triangles into quadrangles.
3135 //           theCrit is used to select a neighbour to fuse with.
3136 //           theMaxAngle is a max angle between element normals at which
3137 //           fusion is still performed.
3138 //=======================================================================
3139
3140 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3141                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3142                                   const double                         theMaxAngle)
3143 {
3144   ClearLastCreated();
3145   myLastCreatedElems.reserve( theElems.size() / 2 );
3146
3147   if ( !theCrit.get() )
3148     return false;
3149
3150   SMESHDS_Mesh * aMesh = GetMeshDS();
3151
3152   // Prepare data for algo: build
3153   // 1. map of elements with their linkIDs
3154   // 2. map of linkIDs with their elements
3155
3156   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3157   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3158   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3159   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3160
3161   TIDSortedElemSet::iterator itElem;
3162   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3163   {
3164     const SMDS_MeshElement* elem = *itElem;
3165     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3166     bool IsTria = ( elem->NbCornerNodes()==3 );
3167     if (!IsTria) continue;
3168
3169     // retrieve element nodes
3170     const SMDS_MeshNode* aNodes [4];
3171     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3172     int i = 0;
3173     while ( i < 3 )
3174       aNodes[ i++ ] = itN->next();
3175     aNodes[ 3 ] = aNodes[ 0 ];
3176
3177     // fill maps
3178     for ( i = 0; i < 3; i++ ) {
3179       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3180       // check if elements sharing a link can be fused
3181       itLE = mapLi_listEl.find( link );
3182       if ( itLE != mapLi_listEl.end() ) {
3183         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3184           continue;
3185         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3186         //if ( FindShape( elem ) != FindShape( elem2 ))
3187         //  continue; // do not fuse triangles laying on different shapes
3188         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3189           continue; // avoid making badly shaped quads
3190         (*itLE).second.push_back( elem );
3191       }
3192       else {
3193         mapLi_listEl[ link ].push_back( elem );
3194       }
3195       mapEl_setLi [ elem ].insert( link );
3196     }
3197   }
3198   // Clean the maps from the links shared by a sole element, ie
3199   // links to which only one element is bound in mapLi_listEl
3200
3201   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3202     int nbElems = (*itLE).second.size();
3203     if ( nbElems < 2  ) {
3204       const SMDS_MeshElement* elem = (*itLE).second.front();
3205       SMESH_TLink link = (*itLE).first;
3206       mapEl_setLi[ elem ].erase( link );
3207       if ( mapEl_setLi[ elem ].empty() )
3208         mapEl_setLi.erase( elem );
3209     }
3210   }
3211
3212   // Algo: fuse triangles into quadrangles
3213
3214   while ( ! mapEl_setLi.empty() ) {
3215     // Look for the start element:
3216     // the element having the least nb of shared links
3217     const SMDS_MeshElement* startElem = 0;
3218     int minNbLinks = 4;
3219     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3220       int nbLinks = (*itEL).second.size();
3221       if ( nbLinks < minNbLinks ) {
3222         startElem = (*itEL).first;
3223         minNbLinks = nbLinks;
3224         if ( minNbLinks == 1 )
3225           break;
3226       }
3227     }
3228
3229     // search elements to fuse starting from startElem or links of elements
3230     // fused earlyer - startLinks
3231     list< SMESH_TLink > startLinks;
3232     while ( startElem || !startLinks.empty() ) {
3233       while ( !startElem && !startLinks.empty() ) {
3234         // Get an element to start, by a link
3235         SMESH_TLink linkId = startLinks.front();
3236         startLinks.pop_front();
3237         itLE = mapLi_listEl.find( linkId );
3238         if ( itLE != mapLi_listEl.end() ) {
3239           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3240           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3241           for ( ; itE != listElem.end() ; itE++ )
3242             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3243               startElem = (*itE);
3244           mapLi_listEl.erase( itLE );
3245         }
3246       }
3247
3248       if ( startElem ) {
3249         // Get candidates to be fused
3250         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3251         const SMESH_TLink *link12 = 0, *link13 = 0;
3252         startElem = 0;
3253         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3254         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3255         ASSERT( !setLi.empty() );
3256         set< SMESH_TLink >::iterator itLi;
3257         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3258         {
3259           const SMESH_TLink & link = (*itLi);
3260           itLE = mapLi_listEl.find( link );
3261           if ( itLE == mapLi_listEl.end() )
3262             continue;
3263
3264           const SMDS_MeshElement* elem = (*itLE).second.front();
3265           if ( elem == tr1 )
3266             elem = (*itLE).second.back();
3267           mapLi_listEl.erase( itLE );
3268           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3269             continue;
3270           if ( tr2 ) {
3271             tr3 = elem;
3272             link13 = &link;
3273           }
3274           else {
3275             tr2 = elem;
3276             link12 = &link;
3277           }
3278
3279           // add other links of elem to list of links to re-start from
3280           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3281           set< SMESH_TLink >::iterator it;
3282           for ( it = links.begin(); it != links.end(); it++ ) {
3283             const SMESH_TLink& link2 = (*it);
3284             if ( link2 != link )
3285               startLinks.push_back( link2 );
3286           }
3287         }
3288
3289         // Get nodes of possible quadrangles
3290         const SMDS_MeshNode *n12 [4], *n13 [4];
3291         bool Ok12 = false, Ok13 = false;
3292         const SMDS_MeshNode *linkNode1, *linkNode2;
3293         if(tr2) {
3294           linkNode1 = link12->first;
3295           linkNode2 = link12->second;
3296           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3297             Ok12 = true;
3298         }
3299         if(tr3) {
3300           linkNode1 = link13->first;
3301           linkNode2 = link13->second;
3302           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3303             Ok13 = true;
3304         }
3305
3306         // Choose a pair to fuse
3307         if ( Ok12 && Ok13 ) {
3308           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3309           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3310           double aBadRate12 = getBadRate( &quad12, theCrit );
3311           double aBadRate13 = getBadRate( &quad13, theCrit );
3312           if (  aBadRate13 < aBadRate12 )
3313             Ok12 = false;
3314           else
3315             Ok13 = false;
3316         }
3317
3318         // Make quadrangles
3319         // and remove fused elems and remove links from the maps
3320         mapEl_setLi.erase( tr1 );
3321         if ( Ok12 )
3322         {
3323           mapEl_setLi.erase( tr2 );
3324           mapLi_listEl.erase( *link12 );
3325           if ( tr1->NbNodes() == 3 )
3326           {
3327             const SMDS_MeshElement* newElem = 0;
3328             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3329             myLastCreatedElems.push_back(newElem);
3330             AddToSameGroups( newElem, tr1, aMesh );
3331             int aShapeId = tr1->getshapeId();
3332             if ( aShapeId )
3333               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3334             aMesh->RemoveElement( tr1 );
3335             aMesh->RemoveElement( tr2 );
3336           }
3337           else {
3338             vector< const SMDS_MeshNode* > N1;
3339             vector< const SMDS_MeshNode* > N2;
3340             getNodesFromTwoTria(tr1,tr2,N1,N2);
3341             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3342             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3343             // i.e. first nodes from both arrays form a new diagonal
3344             const SMDS_MeshNode* aNodes[8];
3345             aNodes[0] = N1[0];
3346             aNodes[1] = N1[1];
3347             aNodes[2] = N2[0];
3348             aNodes[3] = N2[1];
3349             aNodes[4] = N1[3];
3350             aNodes[5] = N2[5];
3351             aNodes[6] = N2[3];
3352             aNodes[7] = N1[5];
3353             const SMDS_MeshElement* newElem = 0;
3354             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3355               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3356                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3357             else
3358               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3359                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3360             myLastCreatedElems.push_back(newElem);
3361             AddToSameGroups( newElem, tr1, aMesh );
3362             int aShapeId = tr1->getshapeId();
3363             if ( aShapeId )
3364               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3365             aMesh->RemoveElement( tr1 );
3366             aMesh->RemoveElement( tr2 );
3367             // remove middle node (9)
3368             if ( N1[4]->NbInverseElements() == 0 )
3369               aMesh->RemoveNode( N1[4] );
3370             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3371               aMesh->RemoveNode( N1[6] );
3372             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3373               aMesh->RemoveNode( N2[6] );
3374           }
3375         }
3376         else if ( Ok13 )
3377         {
3378           mapEl_setLi.erase( tr3 );
3379           mapLi_listEl.erase( *link13 );
3380           if ( tr1->NbNodes() == 3 ) {
3381             const SMDS_MeshElement* newElem = 0;
3382             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3383             myLastCreatedElems.push_back(newElem);
3384             AddToSameGroups( newElem, tr1, aMesh );
3385             int aShapeId = tr1->getshapeId();
3386             if ( aShapeId )
3387               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3388             aMesh->RemoveElement( tr1 );
3389             aMesh->RemoveElement( tr3 );
3390           }
3391           else {
3392             vector< const SMDS_MeshNode* > N1;
3393             vector< const SMDS_MeshNode* > N2;
3394             getNodesFromTwoTria(tr1,tr3,N1,N2);
3395             // now we receive following N1 and N2 (using numeration as above image)
3396             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3397             // i.e. first nodes from both arrays form a new diagonal
3398             const SMDS_MeshNode* aNodes[8];
3399             aNodes[0] = N1[0];
3400             aNodes[1] = N1[1];
3401             aNodes[2] = N2[0];
3402             aNodes[3] = N2[1];
3403             aNodes[4] = N1[3];
3404             aNodes[5] = N2[5];
3405             aNodes[6] = N2[3];
3406             aNodes[7] = N1[5];
3407             const SMDS_MeshElement* newElem = 0;
3408             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3409               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3410                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3411             else
3412               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3413                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3414             myLastCreatedElems.push_back(newElem);
3415             AddToSameGroups( newElem, tr1, aMesh );
3416             int aShapeId = tr1->getshapeId();
3417             if ( aShapeId )
3418               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3419             aMesh->RemoveElement( tr1 );
3420             aMesh->RemoveElement( tr3 );
3421             // remove middle node (9)
3422             if ( N1[4]->NbInverseElements() == 0 )
3423               aMesh->RemoveNode( N1[4] );
3424             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3425               aMesh->RemoveNode( N1[6] );
3426             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3427               aMesh->RemoveNode( N2[6] );
3428           }
3429         }
3430
3431         // Next element to fuse: the rejected one
3432         if ( tr3 )
3433           startElem = Ok12 ? tr3 : tr2;
3434
3435       } // if ( startElem )
3436     } // while ( startElem || !startLinks.empty() )
3437   } // while ( ! mapEl_setLi.empty() )
3438
3439   return true;
3440 }
3441
3442 //================================================================================
3443 /*!
3444  * \brief Return nodes linked to the given one
3445  * \param theNode - the node
3446  * \param linkedNodes - the found nodes
3447  * \param type - the type of elements to check
3448  *
3449  * Medium nodes are ignored
3450  */
3451 //================================================================================
3452
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454                                        TIDSortedElemSet &   linkedNodes,
3455                                        SMDSAbs_ElementType  type )
3456 {
3457   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458   while ( elemIt->more() )
3459   {
3460     const SMDS_MeshElement* elem = elemIt->next();
3461     if(elem->GetType() == SMDSAbs_0DElement)
3462       continue;
3463
3464     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465     if ( elem->GetType() == SMDSAbs_Volume )
3466     {
3467       SMDS_VolumeTool vol( elem );
3468       while ( nodeIt->more() ) {
3469         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470         if ( theNode != n && vol.IsLinked( theNode, n ))
3471           linkedNodes.insert( n );
3472       }
3473     }
3474     else
3475     {
3476       for ( int i = 0; nodeIt->more(); ++i ) {
3477         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478         if ( n == theNode ) {
3479           int iBefore = i - 1;
3480           int iAfter  = i + 1;
3481           if ( elem->IsQuadratic() ) {
3482             int nb = elem->NbNodes() / 2;
3483             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3485           }
3486           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3488         }
3489       }
3490     }
3491   }
3492 }
3493
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose  : pulls theNode toward the center of surrounding nodes directly
3497 //           connected to that node along an element edge
3498 //=======================================================================
3499
3500 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3501                      const Handle(Geom_Surface)&          theSurface,
3502                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3503 {
3504   // find surrounding nodes
3505
3506   TIDSortedElemSet nodeSet;
3507   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3508
3509   // compute new coodrs
3510
3511   double coord[] = { 0., 0., 0. };
3512   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515     if ( theSurface.IsNull() ) { // smooth in 3D
3516       coord[0] += node->X();
3517       coord[1] += node->Y();
3518       coord[2] += node->Z();
3519     }
3520     else { // smooth in 2D
3521       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522       gp_XY* uv = theUVMap[ node ];
3523       coord[0] += uv->X();
3524       coord[1] += uv->Y();
3525     }
3526   }
3527   int nbNodes = nodeSet.size();
3528   if ( !nbNodes )
3529     return;
3530   coord[0] /= nbNodes;
3531   coord[1] /= nbNodes;
3532
3533   if ( !theSurface.IsNull() ) {
3534     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3537     coord[0] = p3d.X();
3538     coord[1] = p3d.Y();
3539     coord[2] = p3d.Z();
3540   }
3541   else
3542     coord[2] /= nbNodes;
3543
3544   // move node
3545
3546   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 }
3548
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3552 //           surrounding elements
3553 //=======================================================================
3554
3555 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3556                       const Handle(Geom_Surface)&          theSurface,
3557                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3558 {
3559   gp_XYZ aNewXYZ(0.,0.,0.);
3560   SMESH::Controls::Area anAreaFunc;
3561   double totalArea = 0.;
3562   int nbElems = 0;
3563
3564   // compute new XYZ
3565
3566   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567   while ( elemIt->more() )
3568   {
3569     const SMDS_MeshElement* elem = elemIt->next();
3570     nbElems++;
3571
3572     gp_XYZ elemCenter(0.,0.,0.);
3573     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575     int nn = elem->NbNodes();
3576     if(elem->IsQuadratic()) nn = nn/2;
3577     int i=0;
3578     //while ( itN->more() ) {
3579     while ( i<nn ) {
3580       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3581       i++;
3582       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583       aNodePoints.push_back( aP );
3584       if ( !theSurface.IsNull() ) { // smooth in 2D
3585         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586         gp_XY* uv = theUVMap[ aNode ];
3587         aP.SetCoord( uv->X(), uv->Y(), 0. );
3588       }
3589       elemCenter += aP;
3590     }
3591     double elemArea = anAreaFunc.GetValue( aNodePoints );
3592     totalArea += elemArea;
3593     elemCenter /= nn;
3594     aNewXYZ += elemCenter * elemArea;
3595   }
3596   aNewXYZ /= totalArea;
3597   if ( !theSurface.IsNull() ) {
3598     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3600   }
3601
3602   // move node
3603
3604   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 }
3606
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose  : return UV of closest projection
3610 //=======================================================================
3611
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613                           const gp_Pnt&     point,
3614                           gp_XY &           result)
3615 {
3616   projector.Perform( point );
3617   if ( projector.IsDone() ) {
3618     double u, v, minVal = DBL_MAX;
3619     for ( int i = projector.NbExt(); i > 0; i-- )
3620       if ( projector.SquareDistance( i ) < minVal ) {
3621         minVal = projector.SquareDistance( i );
3622         projector.Point( i ).Parameter( u, v );
3623       }
3624     result.SetCoord( u, v );
3625     return true;
3626   }
3627   return false;
3628 }
3629
3630 //=======================================================================
3631 //function : Smooth
3632 //purpose  : Smooth theElements during theNbIterations or until a worst
3633 //           element has aspect ratio <= theTgtAspectRatio.
3634 //           Aspect Ratio varies in range [1.0, inf].
3635 //           If theElements is empty, the whole mesh is smoothed.
3636 //           theFixedNodes contains additionally fixed nodes. Nodes built
3637 //           on edges and boundary nodes are always fixed.
3638 //=======================================================================
3639
3640 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3641                                set<const SMDS_MeshNode*> & theFixedNodes,
3642                                const SmoothMethod          theSmoothMethod,
3643                                const int                   theNbIterations,
3644                                double                      theTgtAspectRatio,
3645                                const bool                  the2D)
3646 {
3647   ClearLastCreated();
3648
3649   if ( theTgtAspectRatio < 1.0 )
3650     theTgtAspectRatio = 1.0;
3651
3652   const double disttol = 1.e-16;
3653
3654   SMESH::Controls::AspectRatio aQualityFunc;
3655
3656   SMESHDS_Mesh* aMesh = GetMeshDS();
3657
3658   if ( theElems.empty() ) {
3659     // add all faces to theElems
3660     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3661     while ( fIt->more() ) {
3662       const SMDS_MeshElement* face = fIt->next();
3663       theElems.insert( theElems.end(), face );
3664     }
3665   }
3666   // get all face ids theElems are on
3667   set< int > faceIdSet;
3668   TIDSortedElemSet::iterator itElem;
3669   if ( the2D )
3670     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3671       int fId = FindShape( *itElem );
3672       // check that corresponding submesh exists and a shape is face
3673       if (fId &&
3674           faceIdSet.find( fId ) == faceIdSet.end() &&
3675           aMesh->MeshElements( fId )) {
3676         TopoDS_Shape F = aMesh->IndexToShape( fId );
3677         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3678           faceIdSet.insert( fId );
3679       }
3680     }
3681   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3682
3683   // ===============================================
3684   // smooth elements on each TopoDS_Face separately
3685   // ===============================================
3686
3687   SMESH_MesherHelper helper( *GetMesh() );
3688
3689   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3690   for ( ; fId != faceIdSet.rend(); ++fId )
3691   {
3692     // get face surface and submesh
3693     Handle(Geom_Surface) surface;
3694     SMESHDS_SubMesh* faceSubMesh = 0;
3695     TopoDS_Face face;
3696     double fToler2 = 0;
3697     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3698     bool isUPeriodic = false, isVPeriodic = false;
3699     if ( *fId )
3700     {
3701       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3702       surface = BRep_Tool::Surface( face );
3703       faceSubMesh = aMesh->MeshElements( *fId );
3704       fToler2 = BRep_Tool::Tolerance( face );
3705       fToler2 *= fToler2 * 10.;
3706       isUPeriodic = surface->IsUPeriodic();
3707       // if ( isUPeriodic )
3708       //   surface->UPeriod();
3709       isVPeriodic = surface->IsVPeriodic();
3710       // if ( isVPeriodic )
3711       //   surface->VPeriod();
3712       surface->Bounds( u1, u2, v1, v2 );
3713       helper.SetSubShape( face );
3714     }
3715     // ---------------------------------------------------------
3716     // for elements on a face, find movable and fixed nodes and
3717     // compute UV for them
3718     // ---------------------------------------------------------
3719     bool checkBoundaryNodes = false;
3720     bool isQuadratic = false;
3721     set<const SMDS_MeshNode*> setMovableNodes;
3722     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3723     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3724     list< const SMDS_MeshElement* > elemsOnFace;
3725
3726     Extrema_GenExtPS projector;
3727     GeomAdaptor_Surface surfAdaptor;
3728     if ( !surface.IsNull() ) {
3729       surfAdaptor.Load( surface );
3730       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3731     }
3732     int nbElemOnFace = 0;
3733     itElem = theElems.begin();
3734     // loop on not yet smoothed elements: look for elems on a face
3735     while ( itElem != theElems.end() )
3736     {
3737       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3738         break; // all elements found
3739
3740       const SMDS_MeshElement* elem = *itElem;
3741       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3742            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3743         ++itElem;
3744         continue;
3745       }
3746       elemsOnFace.push_back( elem );
3747       theElems.erase( itElem++ );
3748       nbElemOnFace++;
3749
3750       if ( !isQuadratic )
3751         isQuadratic = elem->IsQuadratic();
3752
3753       // get movable nodes of elem
3754       const SMDS_MeshNode* node;
3755       SMDS_TypeOfPosition posType;
3756       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3757       int nn = 0, nbn =  elem->NbNodes();
3758       if(elem->IsQuadratic())
3759         nbn = nbn/2;
3760       while ( nn++ < nbn ) {
3761         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3762         const SMDS_PositionPtr& pos = node->GetPosition();
3763         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3764         if (posType != SMDS_TOP_EDGE &&
3765             posType != SMDS_TOP_VERTEX &&
3766             theFixedNodes.find( node ) == theFixedNodes.end())
3767         {
3768           // check if all faces around the node are on faceSubMesh
3769           // because a node on edge may be bound to face
3770           bool all = true;
3771           if ( faceSubMesh ) {
3772             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773             while ( eIt->more() && all ) {
3774               const SMDS_MeshElement* e = eIt->next();
3775               all = faceSubMesh->Contains( e );
3776             }
3777           }
3778           if ( all )
3779             setMovableNodes.insert( node );
3780           else
3781             checkBoundaryNodes = true;
3782         }
3783         if ( posType == SMDS_TOP_3DSPACE )
3784           checkBoundaryNodes = true;
3785       }
3786
3787       if ( surface.IsNull() )
3788         continue;
3789
3790       // get nodes to check UV
3791       list< const SMDS_MeshNode* > uvCheckNodes;
3792       const SMDS_MeshNode* nodeInFace = 0;
3793       itN = elem->nodesIterator();
3794       nn = 0; nbn =  elem->NbNodes();
3795       if(elem->IsQuadratic())
3796         nbn = nbn/2;
3797       while ( nn++ < nbn ) {
3798         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3799         if ( node->GetPosition()->GetDim() == 2 )
3800           nodeInFace = node;
3801         if ( uvMap.find( node ) == uvMap.end() )
3802           uvCheckNodes.push_back( node );
3803         // add nodes of elems sharing node
3804         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3805         //         while ( eIt->more() ) {
3806         //           const SMDS_MeshElement* e = eIt->next();
3807         //           if ( e != elem ) {
3808         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3809         //             while ( nIt->more() ) {
3810         //               const SMDS_MeshNode* n =
3811         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3812         //               if ( uvMap.find( n ) == uvMap.end() )
3813         //                 uvCheckNodes.push_back( n );
3814         //             }
3815         //           }
3816         //         }
3817       }
3818       // check UV on face
3819       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3820       for ( ; n != uvCheckNodes.end(); ++n ) {
3821         node = *n;
3822         gp_XY uv( 0, 0 );
3823         const SMDS_PositionPtr& pos = node->GetPosition();
3824         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3825         // get existing UV
3826         if ( pos )
3827         {
3828           bool toCheck = true;
3829           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3830         }
3831         // compute not existing UV
3832         bool project = ( posType == SMDS_TOP_3DSPACE );
3833         // double dist1 = DBL_MAX, dist2 = 0;
3834         // if ( posType != SMDS_TOP_3DSPACE ) {
3835         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3836         //   project = dist1 > fToler2;
3837         // }
3838         if ( project ) { // compute new UV
3839           gp_XY newUV;
3840           gp_Pnt pNode = SMESH_NodeXYZ( node );
3841           if ( !getClosestUV( projector, pNode, newUV )) {
3842             MESSAGE("Node Projection Failed " << node);
3843           }
3844           else {
3845             if ( isUPeriodic )
3846               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3847             if ( isVPeriodic )
3848               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3849             // check new UV
3850             // if ( posType != SMDS_TOP_3DSPACE )
3851             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3852             // if ( dist2 < dist1 )
3853             uv = newUV;
3854           }
3855         }
3856         // store UV in the map
3857         listUV.push_back( uv );
3858         uvMap.insert( make_pair( node, &listUV.back() ));
3859       }
3860     } // loop on not yet smoothed elements
3861
3862     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3863       checkBoundaryNodes = true;
3864
3865     // fix nodes on mesh boundary
3866
3867     if ( checkBoundaryNodes ) {
3868       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3869       map< SMESH_TLink, int >::iterator link_nb;
3870       // put all elements links to linkNbMap
3871       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3872       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3873         const SMDS_MeshElement* elem = (*elemIt);
3874         int nbn =  elem->NbCornerNodes();
3875         // loop on elem links: insert them in linkNbMap
3876         for ( int iN = 0; iN < nbn; ++iN ) {
3877           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3878           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3879           SMESH_TLink link( n1, n2 );
3880           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3881           link_nb->second++;
3882         }
3883       }
3884       // remove nodes that are in links encountered only once from setMovableNodes
3885       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3886         if ( link_nb->second == 1 ) {
3887           setMovableNodes.erase( link_nb->first.node1() );
3888           setMovableNodes.erase( link_nb->first.node2() );
3889         }
3890       }
3891     }
3892
3893     // -----------------------------------------------------
3894     // for nodes on seam edge, compute one more UV ( uvMap2 );
3895     // find movable nodes linked to nodes on seam and which
3896     // are to be smoothed using the second UV ( uvMap2 )
3897     // -----------------------------------------------------
3898
3899     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3900     if ( !surface.IsNull() ) {
3901       TopExp_Explorer eExp( face, TopAbs_EDGE );
3902       for ( ; eExp.More(); eExp.Next() ) {
3903         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3904         if ( !BRep_Tool::IsClosed( edge, face ))
3905           continue;
3906         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3907         if ( !sm ) continue;
3908         // find out which parameter varies for a node on seam
3909         double f,l;
3910         gp_Pnt2d uv1, uv2;
3911         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3912         if ( pcurve.IsNull() ) continue;
3913         uv1 = pcurve->Value( f );
3914         edge.Reverse();
3915         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3916         if ( pcurve.IsNull() ) continue;
3917         uv2 = pcurve->Value( f );
3918         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3919         // assure uv1 < uv2
3920         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3921           std::swap( uv1, uv2 );
3922         // get nodes on seam and its vertices
3923         list< const SMDS_MeshNode* > seamNodes;
3924         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3925         while ( nSeamIt->more() ) {
3926           const SMDS_MeshNode* node = nSeamIt->next();
3927           if ( !isQuadratic || !IsMedium( node ))
3928             seamNodes.push_back( node );
3929         }
3930         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3931         for ( ; vExp.More(); vExp.Next() ) {
3932           sm = aMesh->MeshElements( vExp.Current() );
3933           if ( sm ) {
3934             nSeamIt = sm->GetNodes();
3935             while ( nSeamIt->more() )
3936               seamNodes.push_back( nSeamIt->next() );
3937           }
3938         }
3939         // loop on nodes on seam
3940         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3941         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3942           const SMDS_MeshNode* nSeam = *noSeIt;
3943           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3944           if ( n_uv == uvMap.end() )
3945             continue;
3946           // set the first UV
3947           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3948           // set the second UV
3949           listUV.push_back( *n_uv->second );
3950           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3951           if ( uvMap2.empty() )
3952             uvMap2 = uvMap; // copy the uvMap contents
3953           uvMap2[ nSeam ] = &listUV.back();
3954
3955           // collect movable nodes linked to ones on seam in nodesNearSeam
3956           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3957           while ( eIt->more() ) {
3958             const SMDS_MeshElement* e = eIt->next();
3959             int nbUseMap1 = 0, nbUseMap2 = 0;
3960             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3961             int nn = 0, nbn =  e->NbNodes();
3962             if(e->IsQuadratic()) nbn = nbn/2;
3963             while ( nn++ < nbn )
3964             {
3965               const SMDS_MeshNode* n =
3966                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3967               if (n == nSeam ||
3968                   setMovableNodes.find( n ) == setMovableNodes.end() )
3969                 continue;
3970               // add only nodes being closer to uv2 than to uv1
3971               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3972               //              0.5 * ( n->Y() + nSeam->Y() ),
3973               //              0.5 * ( n->Z() + nSeam->Z() ));
3974               // gp_XY uv;
3975               // getClosestUV( projector, pMid, uv );
3976               double x = uvMap[ n ]->Coord( iPar );
3977               if ( Abs( uv1.Coord( iPar ) - x ) >
3978                    Abs( uv2.Coord( iPar ) - x )) {
3979                 nodesNearSeam.insert( n );
3980                 nbUseMap2++;
3981               }
3982               else
3983                 nbUseMap1++;
3984             }
3985             // for centroidalSmooth all element nodes must
3986             // be on one side of a seam
3987             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3988               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3989               nn = 0;
3990               while ( nn++ < nbn ) {
3991                 const SMDS_MeshNode* n =
3992                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3993                 setMovableNodes.erase( n );
3994               }
3995             }
3996           }
3997         } // loop on nodes on seam
3998       } // loop on edge of a face
3999     } // if ( !face.IsNull() )
4000
4001     if ( setMovableNodes.empty() ) {
4002       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4003       continue; // goto next face
4004     }
4005
4006     // -------------
4007     // SMOOTHING //
4008     // -------------
4009
4010     int it = -1;
4011     double maxRatio = -1., maxDisplacement = -1.;
4012     set<const SMDS_MeshNode*>::iterator nodeToMove;
4013     for ( it = 0; it < theNbIterations; it++ ) {
4014       maxDisplacement = 0.;
4015       nodeToMove = setMovableNodes.begin();
4016       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4017         const SMDS_MeshNode* node = (*nodeToMove);
4018         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4019
4020         // smooth
4021         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4022         if ( theSmoothMethod == LAPLACIAN )
4023           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4024         else
4025           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4026
4027         // node displacement
4028         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4029         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4030         if ( aDispl > maxDisplacement )
4031           maxDisplacement = aDispl;
4032       }
4033       // no node movement => exit
4034       //if ( maxDisplacement < 1.e-16 ) {
4035       if ( maxDisplacement < disttol ) {
4036         MESSAGE("-- no node movement --");
4037         break;
4038       }
4039
4040       // check elements quality
4041       maxRatio  = 0;
4042       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4043       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4044         const SMDS_MeshElement* elem = (*elemIt);
4045         if ( !elem || elem->GetType() != SMDSAbs_Face )
4046           continue;
4047         SMESH::Controls::TSequenceOfXYZ aPoints;
4048         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4049           double aValue = aQualityFunc.GetValue( aPoints );
4050           if ( aValue > maxRatio )
4051             maxRatio = aValue;
4052         }
4053       }
4054       if ( maxRatio <= theTgtAspectRatio ) {
4055         //MESSAGE("-- quality achieved --");
4056         break;
4057       }
4058       if (it+1 == theNbIterations) {
4059         //MESSAGE("-- Iteration limit exceeded --");
4060       }
4061     } // smoothing iterations
4062
4063     // MESSAGE(" Face id: " << *fId <<
4064     //         " Nb iterstions: " << it <<
4065     //         " Displacement: " << maxDisplacement <<
4066     //         " Aspect Ratio " << maxRatio);
4067
4068     // ---------------------------------------
4069     // new nodes positions are computed,
4070     // record movement in DS and set new UV
4071     // ---------------------------------------
4072     nodeToMove = setMovableNodes.begin();
4073     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4074       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4075       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4076       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4077       if ( node_uv != uvMap.end() ) {
4078         gp_XY* uv = node_uv->second;
4079         node->SetPosition
4080           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4081       }
4082     }
4083
4084     // move medium nodes of quadratic elements
4085     if ( isQuadratic )
4086     {
4087       vector<const SMDS_MeshNode*> nodes;
4088       bool checkUV;
4089       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4090       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4091       {
4092         const SMDS_MeshElement* QF = *elemIt;
4093         if ( QF->IsQuadratic() )
4094         {
4095           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4096                         SMDS_MeshElement::iterator() );
4097           nodes.push_back( nodes[0] );
4098           gp_Pnt xyz;
4099           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4100           {
4101             if ( !surface.IsNull() )
4102             {
4103               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4104               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4105               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4106               xyz = surface->Value( uv.X(), uv.Y() );
4107             }
4108             else {
4109               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4110             }
4111             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4112               // we have to move a medium node
4113               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4114           }
4115         }
4116       }
4117     }
4118
4119   } // loop on face ids
4120
4121 }
4122
4123 namespace
4124 {
4125   //=======================================================================
4126   //function : isReverse
4127   //purpose  : Return true if normal of prevNodes is not co-directied with
4128   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4129   //           iNotSame is where prevNodes and nextNodes are different.
4130   //           If result is true then future volume orientation is OK
4131   //=======================================================================
4132
4133   bool isReverse(const SMDS_MeshElement*             face,
4134                  const vector<const SMDS_MeshNode*>& prevNodes,
4135                  const vector<const SMDS_MeshNode*>& nextNodes,
4136                  const int                           iNotSame)
4137   {
4138
4139     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4140     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4141     gp_XYZ extrDir( pN - pP ), faceNorm;
4142     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4143
4144     return faceNorm * extrDir < 0.0;
4145   }
4146
4147   //================================================================================
4148   /*!
4149    * \brief Assure that theElemSets[0] holds elements, not nodes
4150    */
4151   //================================================================================
4152
4153   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4154   {
4155     if ( !theElemSets[0].empty() &&
4156          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4157     {
4158       std::swap( theElemSets[0], theElemSets[1] );
4159     }
4160     else if ( !theElemSets[1].empty() &&
4161               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4162     {
4163       std::swap( theElemSets[0], theElemSets[1] );
4164     }
4165   }
4166 }
4167
4168 //=======================================================================
4169 /*!
4170  * \brief Create elements by sweeping an element
4171  * \param elem - element to sweep
4172  * \param newNodesItVec - nodes generated from each node of the element
4173  * \param newElems - generated elements
4174  * \param nbSteps - number of sweeping steps
4175  * \param srcElements - to append elem for each generated element
4176  */
4177 //=======================================================================
4178
4179 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4180                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4181                                     list<const SMDS_MeshElement*>&        newElems,
4182                                     const size_t                          nbSteps,
4183                                     SMESH_SequenceOfElemPtr&              srcElements)
4184 {
4185   SMESHDS_Mesh* aMesh = GetMeshDS();
4186
4187   const int           nbNodes = elem->NbNodes();
4188   const int         nbCorners = elem->NbCornerNodes();
4189   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4190                                                           polyhedron creation !!! */
4191   // Loop on elem nodes:
4192   // find new nodes and detect same nodes indices
4193   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4194   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4195   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4196   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4197
4198   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4199   vector<int> sames(nbNodes);
4200   vector<bool> isSingleNode(nbNodes);
4201
4202   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4203     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4204     const SMDS_MeshNode*                         node = nnIt->first;
4205     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4206     if ( listNewNodes.empty() )
4207       return;
4208
4209     itNN   [ iNode ] = listNewNodes.begin();
4210     prevNod[ iNode ] = node;
4211     nextNod[ iNode ] = listNewNodes.front();
4212
4213     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4214                                                              corner node of linear */
4215     if ( prevNod[ iNode ] != nextNod [ iNode ])
4216       nbDouble += !isSingleNode[iNode];
4217
4218     if( iNode < nbCorners ) { // check corners only
4219       if ( prevNod[ iNode ] == nextNod [ iNode ])
4220         sames[nbSame++] = iNode;
4221       else
4222         iNotSameNode = iNode;
4223     }
4224   }
4225
4226   if ( nbSame == nbNodes || nbSame > 2) {
4227     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4228     return;
4229   }
4230
4231   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4232   {
4233     // fix nodes order to have bottom normal external
4234     if ( baseType == SMDSEntity_Polygon )
4235     {
4236       std::reverse( itNN.begin(), itNN.end() );
4237       std::reverse( prevNod.begin(), prevNod.end() );
4238       std::reverse( midlNod.begin(), midlNod.end() );
4239       std::reverse( nextNod.begin(), nextNod.end() );
4240       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4241     }
4242     else
4243     {
4244       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4245       SMDS_MeshCell::applyInterlace( ind, itNN );
4246       SMDS_MeshCell::applyInterlace( ind, prevNod );
4247       SMDS_MeshCell::applyInterlace( ind, nextNod );
4248       SMDS_MeshCell::applyInterlace( ind, midlNod );
4249       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4250       if ( nbSame > 0 )
4251       {
4252         sames[nbSame] = iNotSameNode;
4253         for ( int j = 0; j <= nbSame; ++j )
4254           for ( size_t i = 0; i < ind.size(); ++i )
4255             if ( ind[i] == sames[j] )
4256             {
4257               sames[j] = i;
4258               break;
4259             }
4260         iNotSameNode = sames[nbSame];
4261       }
4262     }
4263   }
4264   else if ( elem->GetType() == SMDSAbs_Edge )
4265   {
4266     // orient a new face same as adjacent one
4267     int i1, i2;
4268     const SMDS_MeshElement* e;
4269     TIDSortedElemSet dummy;
4270     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4271         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4272         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4273     {
4274       // there is an adjacent face, check order of nodes in it
4275       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4276       if ( sameOrder )
4277       {
4278         std::swap( itNN[0],    itNN[1] );
4279         std::swap( prevNod[0], prevNod[1] );
4280         std::swap( nextNod[0], nextNod[1] );
4281         std::swap( isSingleNode[0], isSingleNode[1] );
4282         if ( nbSame > 0 )
4283           sames[0] = 1 - sames[0];
4284         iNotSameNode = 1 - iNotSameNode;
4285       }
4286     }
4287   }
4288
4289   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4290   if ( nbSame > 0 ) {
4291     iSameNode    = sames[ nbSame-1 ];
4292     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4293     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4294     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4295   }
4296
4297   if ( baseType == SMDSEntity_Polygon )
4298   {
4299     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4300     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4301   }
4302   else if ( baseType == SMDSEntity_Quad_Polygon )
4303   {
4304     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4305     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4306   }
4307
4308   // make new elements
4309   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4310   {
4311     // get next nodes
4312     for ( iNode = 0; iNode < nbNodes; iNode++ )
4313     {
4314       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4315       nextNod[ iNode ] = *itNN[ iNode ]++;
4316     }
4317
4318     SMDS_MeshElement* aNewElem = 0;
4319     /*if(!elem->IsPoly())*/ {
4320       switch ( baseType ) {
4321       case SMDSEntity_0D:
4322       case SMDSEntity_Node: { // sweep NODE
4323         if ( nbSame == 0 ) {
4324           if ( isSingleNode[0] )
4325             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4326           else
4327             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4328         }
4329         else
4330           return;
4331         break;
4332       }
4333       case SMDSEntity_Edge: { // sweep EDGE
4334         if ( nbDouble == 0 )
4335         {
4336           if ( nbSame == 0 ) // ---> quadrangle
4337             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4338                                       nextNod[ 1 ], nextNod[ 0 ] );
4339           else               // ---> triangle
4340             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4341                                       nextNod[ iNotSameNode ] );
4342         }
4343         else                 // ---> polygon
4344         {
4345           vector<const SMDS_MeshNode*> poly_nodes;
4346           poly_nodes.push_back( prevNod[0] );
4347           poly_nodes.push_back( prevNod[1] );
4348           if ( prevNod[1] != nextNod[1] )
4349           {
4350             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4351             poly_nodes.push_back( nextNod[1] );
4352           }
4353           if ( prevNod[0] != nextNod[0] )
4354           {
4355             poly_nodes.push_back( nextNod[0] );
4356             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4357           }
4358           switch ( poly_nodes.size() ) {
4359           case 3:
4360             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4361             break;
4362           case 4:
4363             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4364                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4365             break;
4366           default:
4367             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4368           }
4369         }
4370         break;
4371       }
4372       case SMDSEntity_Triangle: // TRIANGLE --->
4373       {
4374         if ( nbDouble > 0 ) break;
4375         if ( nbSame == 0 )       // ---> pentahedron
4376           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4378
4379         else if ( nbSame == 1 )  // ---> pyramid
4380           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4381                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4382                                        nextNod[ iSameNode ]);
4383
4384         else // 2 same nodes:       ---> tetrahedron
4385           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4386                                        nextNod[ iNotSameNode ]);
4387         break;
4388       }
4389       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4390       {
4391         if ( nbSame == 2 )
4392           return;
4393         if ( nbDouble+nbSame == 2 )
4394         {
4395           if(nbSame==0) {      // ---> quadratic quadrangle
4396             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4397                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4398           }
4399           else { //(nbSame==1) // ---> quadratic triangle
4400             if(sames[0]==2) {
4401               return; // medium node on axis
4402             }
4403             else if(sames[0]==0)
4404               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4405                                         prevNod[2], midlNod[1], nextNod[2] );
4406             else // sames[0]==1
4407               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4408                                         prevNod[2], nextNod[2], midlNod[0]);
4409           }
4410         }
4411         else if ( nbDouble == 3 )
4412         {
4413           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4414             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4415                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4416           }
4417         }
4418         else
4419           return;
4420         break;
4421       }
4422       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4423         if ( nbDouble > 0 ) break;
4424
4425         if ( nbSame == 0 )       // ---> hexahedron
4426           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4427                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4428
4429         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4430           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4431                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4432                                        nextNod[ iSameNode ]);
4433           newElems.push_back( aNewElem );
4434           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4435                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4436                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4437         }
4438         else if ( nbSame == 2 ) { // ---> pentahedron
4439           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4440             // iBeforeSame is same too
4441             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4442                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4443                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4444           else
4445             // iAfterSame is same too
4446             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4447                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4448                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4449         }
4450         break;
4451       }
4452       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4453       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4454         if ( nbDouble+nbSame != 3 ) break;
4455         if(nbSame==0) {
4456           // --->  pentahedron with 15 nodes
4457           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4458                                        nextNod[0], nextNod[1], nextNod[2],
4459                                        prevNod[3], prevNod[4], prevNod[5],
4460                                        nextNod[3], nextNod[4], nextNod[5],
4461                                        midlNod[0], midlNod[1], midlNod[2]);
4462         }
4463         else if(nbSame==1) {
4464           // --->  2d order pyramid of 13 nodes
4465           int apex = iSameNode;
4466           int i0 = ( apex + 1 ) % nbCorners;
4467           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4468           int i0a = apex + 3;
4469           int i1a = i1 + 3;
4470           int i01 = i0 + 3;
4471           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4472                                       nextNod[i0], nextNod[i1], prevNod[apex],
4473                                       prevNod[i01], midlNod[i0],
4474                                       nextNod[i01], midlNod[i1],
4475                                       prevNod[i1a], prevNod[i0a],
4476                                       nextNod[i0a], nextNod[i1a]);
4477         }
4478         else if(nbSame==2) {
4479           // --->  2d order tetrahedron of 10 nodes
4480           int n1 = iNotSameNode;
4481           int n2 = ( n1 + 1             ) % nbCorners;
4482           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4483           int n12 = n1 + 3;
4484           int n23 = n2 + 3;
4485           int n31 = n3 + 3;
4486           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4487                                        prevNod[n12], prevNod[n23], prevNod[n31],
4488                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4489         }
4490         break;
4491       }
4492       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4493         if( nbSame == 0 ) {
4494           if ( nbDouble != 4 ) break;
4495           // --->  hexahedron with 20 nodes
4496           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4497                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4498                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4499                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4500                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4501         }
4502         else if(nbSame==1) {
4503           // ---> pyramid + pentahedron - can not be created since it is needed
4504           // additional middle node at the center of face
4505           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4506           return;
4507         }
4508         else if( nbSame == 2 ) {
4509           if ( nbDouble != 2 ) break;
4510           // --->  2d order Pentahedron with 15 nodes
4511           int n1,n2,n4,n5;
4512           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4513             // iBeforeSame is same too
4514             n1 = iBeforeSame;
4515             n2 = iOpposSame;
4516             n4 = iSameNode;
4517             n5 = iAfterSame;
4518           }
4519           else {
4520             // iAfterSame is same too
4521             n1 = iSameNode;
4522             n2 = iBeforeSame;
4523             n4 = iAfterSame;
4524             n5 = iOpposSame;
4525           }
4526           int n12 = n2 + 4;
4527           int n45 = n4 + 4;
4528           int n14 = n1 + 4;
4529           int n25 = n5 + 4;
4530           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4531                                        prevNod[n4], prevNod[n5], nextNod[n5],
4532                                        prevNod[n12], midlNod[n2], nextNod[n12],
4533                                        prevNod[n45], midlNod[n5], nextNod[n45],
4534                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4535         }
4536         break;
4537       }
4538       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4539
4540         if( nbSame == 0 && nbDouble == 9 ) {
4541           // --->  tri-quadratic hexahedron with 27 nodes
4542           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4543                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4544                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4545                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4546                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4547                                        prevNod[8], // bottom center
4548                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4549                                        nextNod[8], // top center
4550                                        midlNod[8]);// elem center
4551         }
4552         else
4553         {
4554           return;
4555         }
4556         break;
4557       }
4558       case SMDSEntity_Polygon: { // sweep POLYGON
4559
4560         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4561           // --->  hexagonal prism
4562           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4563                                        prevNod[3], prevNod[4], prevNod[5],
4564                                        nextNod[0], nextNod[1], nextNod[2],
4565                                        nextNod[3], nextNod[4], nextNod[5]);
4566         }
4567         break;
4568       }
4569       case SMDSEntity_Ball:
4570         return;
4571
4572       default:
4573         break;
4574       } // switch ( baseType )
4575     } // scope
4576
4577     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4578     {
4579       if ( baseType != SMDSEntity_Polygon )
4580       {
4581         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4582         SMDS_MeshCell::applyInterlace( ind, prevNod );
4583         SMDS_MeshCell::applyInterlace( ind, nextNod );
4584         SMDS_MeshCell::applyInterlace( ind, midlNod );
4585         SMDS_MeshCell::applyInterlace( ind, itNN );
4586         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4587         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4588       }
4589       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4590       vector<int> quantities (nbNodes + 2);
4591       polyedre_nodes.clear();
4592       quantities.clear();
4593
4594       // bottom of prism
4595       for (int inode = 0; inode < nbNodes; inode++)
4596         polyedre_nodes.push_back( prevNod[inode] );
4597       quantities.push_back( nbNodes );
4598
4599       // top of prism
4600       polyedre_nodes.push_back( nextNod[0] );
4601       for (int inode = nbNodes; inode-1; --inode )
4602         polyedre_nodes.push_back( nextNod[inode-1] );
4603       quantities.push_back( nbNodes );
4604
4605       // side faces
4606       // 3--6--2
4607       // |     |
4608       // 7     5
4609       // |     |
4610       // 0--4--1
4611       const int iQuad = elem->IsQuadratic();
4612       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4613       {
4614         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4615         int inextface = (iface+1+iQuad) % nbNodes;
4616         int imid      = (iface+1) % nbNodes;
4617         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4618         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4619         polyedre_nodes.push_back( prevNod[iface] );             // 1
4620         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4621         {
4622           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4623           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4624         }
4625         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4626         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4627         {
4628           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4629           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4630         }
4631         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4632         if ( nbFaceNodes > 2 )
4633           quantities.push_back( nbFaceNodes );
4634         else // degenerated face
4635           polyedre_nodes.resize( prevNbNodes );
4636       }
4637       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4638
4639     } // try to create a polyherdal prism
4640
4641     if ( aNewElem ) {
4642       newElems.push_back( aNewElem );
4643       myLastCreatedElems.push_back(aNewElem);
4644       srcElements.push_back( elem );
4645     }
4646
4647     // set new prev nodes
4648     for ( iNode = 0; iNode < nbNodes; iNode++ )
4649       prevNod[ iNode ] = nextNod[ iNode ];
4650
4651   } // loop on steps
4652 }
4653
4654 //=======================================================================
4655 /*!
4656  * \brief Create 1D and 2D elements around swept elements
4657  * \param mapNewNodes - source nodes and ones generated from them
4658  * \param newElemsMap - source elements and ones generated from them
4659  * \param elemNewNodesMap - nodes generated from each node of each element
4660  * \param elemSet - all swept elements
4661  * \param nbSteps - number of sweeping steps
4662  * \param srcElements - to append elem for each generated element
4663  */
4664 //=======================================================================
4665
4666 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4667                                   TTElemOfElemListMap &    newElemsMap,
4668                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4669                                   TIDSortedElemSet&        elemSet,
4670                                   const int                nbSteps,
4671                                   SMESH_SequenceOfElemPtr& srcElements)
4672 {
4673   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4674   SMESHDS_Mesh* aMesh = GetMeshDS();
4675
4676   // Find nodes belonging to only one initial element - sweep them into edges.
4677
4678   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4679   for ( ; nList != mapNewNodes.end(); nList++ )
4680   {
4681     const SMDS_MeshNode* node =
4682       static_cast<const SMDS_MeshNode*>( nList->first );
4683     if ( newElemsMap.count( node ))
4684       continue; // node was extruded into edge
4685     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4686     int nbInitElems = 0;
4687     const SMDS_MeshElement* el = 0;
4688     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4689     while ( eIt->more() && nbInitElems < 2 ) {
4690       const SMDS_MeshElement* e = eIt->next();
4691       SMDSAbs_ElementType  type = e->GetType();
4692       if ( type == SMDSAbs_Volume ||
4693            type < highType ||
4694            !elemSet.count(e))
4695         continue;
4696       if ( type > highType ) {
4697         nbInitElems = 0;
4698         highType    = type;
4699       }
4700       el = e;
4701       ++nbInitElems;
4702     }
4703     if ( nbInitElems == 1 ) {
4704       bool NotCreateEdge = el && el->IsMediumNode(node);
4705       if(!NotCreateEdge) {
4706         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4707         list<const SMDS_MeshElement*> newEdges;
4708         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4709       }
4710     }
4711   }
4712
4713   // Make a ceiling for each element ie an equal element of last new nodes.
4714   // Find free links of faces - make edges and sweep them into faces.
4715
4716   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4717
4718   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4719   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4720   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4721   {
4722     const SMDS_MeshElement* elem = itElem->first;
4723     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4724
4725     if(itElem->second.size()==0) continue;
4726
4727     const bool isQuadratic = elem->IsQuadratic();
4728
4729     if ( elem->GetType() == SMDSAbs_Edge ) {
4730       // create a ceiling edge
4731       if ( !isQuadratic ) {
4732         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4733                                vecNewNodes[ 1 ]->second.back())) {
4734           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735                                                       vecNewNodes[ 1 ]->second.back()));
4736           srcElements.push_back( elem );
4737         }
4738       }
4739       else {
4740         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4741                                vecNewNodes[ 1 ]->second.back(),
4742                                vecNewNodes[ 2 ]->second.back())) {
4743           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4744                                                       vecNewNodes[ 1 ]->second.back(),
4745                                                       vecNewNodes[ 2 ]->second.back()));
4746           srcElements.push_back( elem );
4747         }
4748       }
4749     }
4750     if ( elem->GetType() != SMDSAbs_Face )
4751       continue;
4752
4753     bool hasFreeLinks = false;
4754
4755     TIDSortedElemSet avoidSet;
4756     avoidSet.insert( elem );
4757
4758     set<const SMDS_MeshNode*> aFaceLastNodes;
4759     int iNode, nbNodes = vecNewNodes.size();
4760     if ( !isQuadratic ) {
4761       // loop on the face nodes
4762       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4763         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4764         // look for free links of the face
4765         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4766         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4767         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4768         // check if a link n1-n2 is free
4769         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4770           hasFreeLinks = true;
4771           // make a new edge and a ceiling for a new edge
4772           const SMDS_MeshElement* edge;
4773           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4774             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4775             srcElements.push_back( myLastCreatedElems.back() );
4776           }
4777           n1 = vecNewNodes[ iNode ]->second.back();
4778           n2 = vecNewNodes[ iNext ]->second.back();
4779           if ( !aMesh->FindEdge( n1, n2 )) {
4780             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4781             srcElements.push_back( edge );
4782           }
4783         }
4784       }
4785     }
4786     else { // elem is quadratic face
4787       int nbn = nbNodes/2;
4788       for ( iNode = 0; iNode < nbn; iNode++ ) {
4789         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4791         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4792         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4793         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4794         // check if a link is free
4795         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4796              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4797              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4798           hasFreeLinks = true;
4799           // make an edge and a ceiling for a new edge
4800           // find medium node
4801           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4802             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4803             srcElements.push_back( elem );
4804           }
4805           n1 = vecNewNodes[ iNode ]->second.back();
4806           n2 = vecNewNodes[ iNext ]->second.back();
4807           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4808           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4809             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4810             srcElements.push_back( elem );
4811           }
4812         }
4813       }
4814       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4815         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4816       }
4817     }
4818
4819     // sweep free links into faces
4820
4821     if ( hasFreeLinks ) {
4822       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4823       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4824
4825       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4826       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4827       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4828         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4829         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4830       }
4831       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4832         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4833         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4834       }
4835       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4836         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4837         std::advance( v, volNb );
4838         // find indices of free faces of a volume and their source edges
4839         list< int > freeInd;
4840         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4841         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4842         int iF, nbF = vTool.NbFaces();
4843         for ( iF = 0; iF < nbF; iF ++ ) {
4844           if ( vTool.IsFreeFace( iF ) &&
4845                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4846                initNodeSet != faceNodeSet) // except an initial face
4847           {
4848             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4849               continue;
4850             if ( faceNodeSet == initNodeSetNoCenter )
4851               continue;
4852             freeInd.push_back( iF );
4853             // find source edge of a free face iF
4854             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4855             vector<const SMDS_MeshNode*>::iterator lastCommom;
4856             commonNodes.resize( nbNodes, 0 );
4857             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4858                                                 initNodeSet.begin(), initNodeSet.end(),
4859                                                 commonNodes.begin());
4860             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4861               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4862             else
4863               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4864 #ifdef _DEBUG_
4865             if ( !srcEdges.back() )
4866             {
4867               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4868                    << iF << " of volume #" << vTool.ID() << endl;
4869             }
4870 #endif
4871           }
4872         }
4873         if ( freeInd.empty() )
4874           continue;
4875
4876         // create wall faces for all steps;
4877         // if such a face has been already created by sweep of edge,
4878         // assure that its orientation is OK
4879         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4880         {
4881           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4882           vTool.SetExternalNormal();
4883           const int nextShift = vTool.IsForward() ? +1 : -1;
4884           list< int >::iterator ind = freeInd.begin();
4885           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4886           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4887           {
4888             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4889             int nbn = vTool.NbFaceNodes( *ind );
4890             const SMDS_MeshElement * f = 0;
4891             if ( nbn == 3 )              ///// triangle
4892             {
4893               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4894               if ( !f ||
4895                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4896               {
4897                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4898                                                      nodes[ 1 ],
4899                                                      nodes[ 1 + nextShift ] };
4900                 if ( f )
4901                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4902                 else
4903                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4904                                                                newOrder[ 2 ] ));
4905               }
4906             }
4907             else if ( nbn == 4 )       ///// quadrangle
4908             {
4909               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4910               if ( !f ||
4911                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4912               {
4913                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4914                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4915                 if ( f )
4916                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4917                 else
4918                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4919                                                                newOrder[ 2 ], newOrder[ 3 ]));
4920               }
4921             }
4922             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4923             {
4924               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4925               if ( !f ||
4926                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4927               {
4928                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4929                                                      nodes[2],
4930                                                      nodes[2 + 2*nextShift],
4931                                                      nodes[3 - 2*nextShift],
4932                                                      nodes[3],
4933                                                      nodes[3 + 2*nextShift]};
4934                 if ( f )
4935                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4936                 else
4937                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4938                                                                newOrder[ 1 ],
4939                                                                newOrder[ 2 ],
4940                                                                newOrder[ 3 ],
4941                                                                newOrder[ 4 ],
4942                                                                newOrder[ 5 ] ));
4943               }
4944             }
4945             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4946             {
4947               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4948                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4949               if ( !f ||
4950                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4951               {
4952                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4953                                                      nodes[4 - 2*nextShift],
4954                                                      nodes[4],
4955                                                      nodes[4 + 2*nextShift],
4956                                                      nodes[1],
4957                                                      nodes[5 - 2*nextShift],
4958                                                      nodes[5],
4959                                                      nodes[5 + 2*nextShift] };
4960                 if ( f )
4961                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4962                 else
4963                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4964                                                               newOrder[ 2 ], newOrder[ 3 ],
4965                                                               newOrder[ 4 ], newOrder[ 5 ],
4966                                                               newOrder[ 6 ], newOrder[ 7 ]));
4967               }
4968             }
4969             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4970             {
4971               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4972                                       SMDSAbs_Face, /*noMedium=*/false);
4973               if ( !f ||
4974                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4975               {
4976                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4977                                                      nodes[4 - 2*nextShift],
4978                                                      nodes[4],
4979                                                      nodes[4 + 2*nextShift],
4980                                                      nodes[1],
4981                                                      nodes[5 - 2*nextShift],
4982                                                      nodes[5],
4983                                                      nodes[5 + 2*nextShift],
4984                                                      nodes[8] };
4985                 if ( f )
4986                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4987                 else
4988                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4989                                                               newOrder[ 2 ], newOrder[ 3 ],
4990                                                               newOrder[ 4 ], newOrder[ 5 ],
4991                                                               newOrder[ 6 ], newOrder[ 7 ],
4992                                                               newOrder[ 8 ]));
4993               }
4994             }
4995             else  //////// polygon
4996             {
4997               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4998               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4999               if ( !f ||
5000                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5001               {
5002                 if ( !vTool.IsForward() )
5003                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5004                 if ( f )
5005                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5006                 else
5007                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5008               }
5009             }
5010
5011             while ( srcElements.size() < myLastCreatedElems.size() )
5012               srcElements.push_back( *srcEdge );
5013
5014           }  // loop on free faces
5015
5016           // go to the next volume
5017           iVol = 0;
5018           while ( iVol++ < nbVolumesByStep ) v++;
5019
5020         } // loop on steps
5021       } // loop on volumes of one step
5022     } // sweep free links into faces
5023
5024     // Make a ceiling face with a normal external to a volume
5025
5026     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5027     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5028     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5029
5030     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5031       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5032       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5033     }
5034     if ( iF >= 0 )
5035     {
5036       lastVol.SetExternalNormal();
5037       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5038       const               int nbn = lastVol.NbFaceNodes( iF );
5039       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5040       if ( !hasFreeLinks ||
5041            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5042       {
5043         const vector<int>& interlace =
5044           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5045         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5046
5047         AddElement( nodeVec, anyFace.Init( elem ));
5048
5049         while ( srcElements.size() < myLastCreatedElems.size() )
5050           srcElements.push_back( elem );
5051       }
5052     }
5053   } // loop on swept elements
5054 }
5055
5056 //=======================================================================
5057 //function : RotationSweep
5058 //purpose  :
5059 //=======================================================================
5060
5061 SMESH_MeshEditor::PGroupIDs
5062 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5063                                 const gp_Ax1&      theAxis,
5064                                 const double       theAngle,
5065                                 const int          theNbSteps,
5066                                 const double       theTol,
5067                                 const bool         theMakeGroups,
5068                                 const bool         theMakeWalls)
5069 {
5070   ClearLastCreated();
5071
5072   setElemsFirst( theElemSets );
5073   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5074   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5075
5076   // source elements for each generated one
5077   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5078   srcElems.reserve( theElemSets[0].size() );
5079   srcNodes.reserve( theElemSets[1].size() );
5080
5081   gp_Trsf aTrsf;
5082   aTrsf.SetRotation( theAxis, theAngle );
5083   gp_Trsf aTrsf2;
5084   aTrsf2.SetRotation( theAxis, theAngle/2. );
5085
5086   gp_Lin aLine( theAxis );
5087   double aSqTol = theTol * theTol;
5088
5089   SMESHDS_Mesh* aMesh = GetMeshDS();
5090
5091   TNodeOfNodeListMap mapNewNodes;
5092   TElemOfVecOfNnlmiMap mapElemNewNodes;
5093   TTElemOfElemListMap newElemsMap;
5094
5095   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5096                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5097                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5098   // loop on theElemSets
5099   TIDSortedElemSet::iterator itElem;
5100   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5101   {
5102     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5103     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5104       const SMDS_MeshElement* elem = *itElem;
5105       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5106         continue;
5107       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5108       newNodesItVec.reserve( elem->NbNodes() );
5109
5110       // loop on elem nodes
5111       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5112       while ( itN->more() )
5113       {
5114         const SMDS_MeshNode* node = cast2Node( itN->next() );
5115
5116         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5117         double coord[3];
5118         aXYZ.Coord( coord[0], coord[1], coord[2] );
5119         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5120
5121         // check if a node has been already sweeped
5122         TNodeOfNodeListMapItr nIt =
5123           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5124         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5125         if ( listNewNodes.empty() )
5126         {
5127           // check if we are to create medium nodes between corner ones
5128           bool needMediumNodes = false;
5129           if ( isQuadraticMesh )
5130           {
5131             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5132             while (it->more() && !needMediumNodes )
5133             {
5134               const SMDS_MeshElement* invElem = it->next();
5135               if ( invElem != elem && !theElems.count( invElem )) continue;
5136               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5137               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5138                 needMediumNodes = true;
5139             }
5140           }
5141
5142           // make new nodes
5143           const SMDS_MeshNode * newNode = node;
5144           for ( int i = 0; i < theNbSteps; i++ ) {
5145             if ( !isOnAxis ) {
5146               if ( needMediumNodes )  // create a medium node
5147               {
5148                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5149                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5150                 myLastCreatedNodes.push_back(newNode);
5151                 srcNodes.push_back( node );
5152                 listNewNodes.push_back( newNode );
5153                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5154               }
5155               else {
5156                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5157               }
5158               // create a corner node
5159               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5160               myLastCreatedNodes.push_back(newNode);
5161               srcNodes.push_back( node );
5162               listNewNodes.push_back( newNode );
5163             }
5164             else {
5165               listNewNodes.push_back( newNode );
5166               // if ( needMediumNodes )
5167               //   listNewNodes.push_back( newNode );
5168             }
5169           }
5170         }
5171         newNodesItVec.push_back( nIt );
5172       }
5173       // make new elements
5174       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5175     }
5176   }
5177
5178   if ( theMakeWalls )
5179     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5180
5181   PGroupIDs newGroupIDs;
5182   if ( theMakeGroups )
5183     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5184
5185   return newGroupIDs;
5186 }
5187
5188 //=======================================================================
5189 //function : ExtrusParam
5190 //purpose  : standard construction
5191 //=======================================================================
5192
5193 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5194                                             const int                theNbSteps,
5195                                             const std::list<double>& theScales,
5196                                             const std::list<double>& theAngles,
5197                                             const gp_XYZ*            theBasePoint,
5198                                             const int                theFlags,
5199                                             const double             theTolerance):
5200   myDir( theStep ),
5201   myBaseP( Precision::Infinite(), 0, 0 ),
5202   myFlags( theFlags ),
5203   myTolerance( theTolerance ),
5204   myElemsToUse( NULL )
5205 {
5206   mySteps = new TColStd_HSequenceOfReal;
5207   const double stepSize = theStep.Magnitude();
5208   for (int i=1; i<=theNbSteps; i++ )
5209     mySteps->Append( stepSize );
5210
5211   if ( !theScales.empty() )
5212   {
5213     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5214       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5215
5216     // add medium scales
5217     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5218     myScales.reserve( theNbSteps * 2 );
5219     myScales.push_back( 0.5 * ( *s1 + 1. ));
5220     myScales.push_back( *s1 );
5221     for ( ; s2 != theScales.end(); s1 = s2++ )
5222     {
5223       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5224       myScales.push_back( *s2 );
5225     }
5226   }
5227
5228   if ( !theAngles.empty() )
5229   {
5230     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5231     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5232       linearAngleVariation( theNbSteps, angles );
5233
5234     // accumulate angles
5235     double angle = 0;
5236     int nbAngles = 0;
5237     std::list<double>::iterator a1 = angles.begin(), a2;
5238     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5239     {
5240       angle += *a1;
5241       *a1 = angle;
5242     }
5243     while ( nbAngles++ < theNbSteps )
5244       angles.push_back( angles.back() );
5245
5246     // add medium angles
5247     a2 = angles.begin(), a1 = a2++;
5248     myAngles.push_back( 0.5 * *a1 );
5249     myAngles.push_back( *a1 );
5250     for ( ; a2 != angles.end(); a1 = a2++ )
5251     {
5252       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5253       myAngles.push_back( *a2 );
5254     }
5255   }
5256
5257   if ( theBasePoint )
5258   {
5259     myBaseP = *theBasePoint;
5260   }
5261
5262   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5263       ( theTolerance > 0 ))
5264   {
5265     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5266   }
5267   else
5268   {
5269     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5270   }
5271 }
5272
5273 //=======================================================================
5274 //function : ExtrusParam
5275 //purpose  : steps are given explicitly
5276 //=======================================================================
5277
5278 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5279                                             Handle(TColStd_HSequenceOfReal) theSteps,
5280                                             const int                       theFlags,
5281                                             const double                    theTolerance):
5282   myDir( theDir ),
5283   mySteps( theSteps ),
5284   myFlags( theFlags ),
5285   myTolerance( theTolerance ),
5286   myElemsToUse( NULL )
5287 {
5288   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5289       ( theTolerance > 0 ))
5290   {
5291     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5292   }
5293   else
5294   {
5295     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5296   }
5297 }
5298
5299 //=======================================================================
5300 //function : ExtrusParam
5301 //purpose  : for extrusion by normal
5302 //=======================================================================
5303
5304 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5305                                             const int    theNbSteps,
5306                                             const int    theFlags,
5307                                             const int    theDim ):
5308   myDir( 1,0,0 ),
5309   mySteps( new TColStd_HSequenceOfReal ),
5310   myFlags( theFlags ),
5311   myTolerance( 0 ),
5312   myElemsToUse( NULL )
5313 {
5314   for (int i = 0; i < theNbSteps; i++ )
5315     mySteps->Append( theStepSize );
5316
5317   if ( theDim == 1 )
5318   {
5319     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5320   }
5321   else
5322   {
5323     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5324   }
5325 }
5326
5327 //=======================================================================
5328 //function : ExtrusParam
5329 //purpose  : for extrusion along path
5330 //=======================================================================
5331
5332 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5333                                             const gp_Pnt*                   theBasePoint,
5334                                             const std::list<double>&        theScales,
5335                                             const bool                      theMakeGroups )
5336   : myBaseP( Precision::Infinite(), 0, 0 ),
5337     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5338     myPathPoints( thePoints )
5339 {
5340   if ( theBasePoint )
5341   {
5342     myBaseP = theBasePoint->XYZ();
5343   }
5344
5345   if ( !theScales.empty() )
5346   {
5347     // add medium scales
5348     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5349     myScales.reserve( thePoints.size() * 2 );
5350     myScales.push_back( 0.5 * ( 1. + *s1 ));
5351     myScales.push_back( *s1 );
5352     for ( ; s2 != theScales.end(); s1 = s2++ )
5353     {
5354       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5355       myScales.push_back( *s2 );
5356     }
5357   }
5358
5359   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5360 }
5361
5362 //=======================================================================
5363 //function : ExtrusParam::SetElementsToUse
5364 //purpose  : stores elements to use for extrusion by normal, depending on
5365 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5366 //           define myBaseP for scaling
5367 //=======================================================================
5368
5369 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5370                                                       const TIDSortedElemSet& nodes )
5371 {
5372   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5373
5374   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5375   {
5376     myBaseP.SetCoord( 0.,0.,0. );
5377     TIDSortedElemSet newNodes;
5378
5379     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5380     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5381     {
5382       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5383       TIDSortedElemSet::const_iterator itElem = elements.begin();
5384       for ( ; itElem != elements.end(); itElem++ )
5385       {
5386         const SMDS_MeshElement* elem = *itElem;
5387         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5388         while ( itN->more() ) {
5389           const SMDS_MeshElement* node = itN->next();
5390           if ( newNodes.insert( node ).second )
5391             myBaseP += SMESH_NodeXYZ( node );
5392         }
5393       }
5394     }
5395     myBaseP /= newNodes.size();
5396   }
5397 }
5398
5399 //=======================================================================
5400 //function : ExtrusParam::beginStepIter
5401 //purpose  : prepare iteration on steps
5402 //=======================================================================
5403
5404 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5405 {
5406   myWithMediumNodes = withMediumNodes;
5407   myNextStep = 1;
5408   myCurSteps.clear();
5409 }
5410 //=======================================================================
5411 //function : ExtrusParam::moreSteps
5412 //purpose  : are there more steps?
5413 //=======================================================================
5414
5415 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5416 {
5417   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5418 }
5419 //=======================================================================
5420 //function : ExtrusParam::nextStep
5421 //purpose  : returns the next step
5422 //=======================================================================
5423
5424 double SMESH_MeshEditor::ExtrusParam::nextStep()
5425 {
5426   double res = 0;
5427   if ( !myCurSteps.empty() )
5428   {
5429     res = myCurSteps.back();
5430     myCurSteps.pop_back();
5431   }
5432   else if ( myNextStep <= mySteps->Length() )
5433   {
5434     myCurSteps.push_back( mySteps->Value( myNextStep ));
5435     ++myNextStep;
5436     if ( myWithMediumNodes )
5437     {
5438       myCurSteps.back() /= 2.;
5439       myCurSteps.push_back( myCurSteps.back() );
5440     }
5441     res = nextStep();
5442   }
5443   return res;
5444 }
5445
5446 //=======================================================================
5447 //function : ExtrusParam::makeNodesByDir
5448 //purpose  : create nodes for standard extrusion
5449 //=======================================================================
5450
5451 int SMESH_MeshEditor::ExtrusParam::
5452 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5453                 const SMDS_MeshNode*              srcNode,
5454                 std::list<const SMDS_MeshNode*> & newNodes,
5455                 const bool                        makeMediumNodes)
5456 {
5457   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5458
5459   int nbNodes = 0;
5460   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5461   {
5462     p += myDir.XYZ() * nextStep();
5463     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5464     newNodes.push_back( newNode );
5465   }
5466
5467   if ( !myScales.empty() || !myAngles.empty() )
5468   {
5469     gp_XYZ  center = myBaseP;
5470     gp_Ax1  ratationAxis( center, myDir );
5471     gp_Trsf rotation;
5472
5473     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5474     size_t i = !makeMediumNodes;
5475     for ( beginStepIter( makeMediumNodes );
5476           moreSteps();
5477           ++nIt, i += 1 + !makeMediumNodes )
5478     {
5479       center += myDir.XYZ() * nextStep();
5480
5481       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5482       bool moved = false;
5483       if ( i < myScales.size() )
5484       {
5485         xyz = ( myScales[i] * ( xyz - center )) + center;
5486         moved = true;
5487       }
5488       if ( !myAngles.empty() )
5489       {
5490         rotation.SetRotation( ratationAxis, myAngles[i] );
5491         rotation.Transforms( xyz );
5492         moved = true;
5493       }
5494       if ( moved )
5495         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5496       else
5497         break;
5498     }
5499   }
5500   return nbNodes;
5501 }
5502
5503 //=======================================================================
5504 //function : ExtrusParam::makeNodesByDirAndSew
5505 //purpose  : create nodes for standard extrusion with sewing
5506 //=======================================================================
5507
5508 int SMESH_MeshEditor::ExtrusParam::
5509 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5510                       const SMDS_MeshNode*              srcNode,
5511                       std::list<const SMDS_MeshNode*> & newNodes,
5512                       const bool                        makeMediumNodes)
5513 {
5514   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5515
5516   int nbNodes = 0;
5517   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5518   {
5519     P1 += myDir.XYZ() * nextStep();
5520
5521     // try to search in sequence of existing nodes
5522     // if myNodes.size()>0 we 'nave to use given sequence
5523     // else - use all nodes of mesh
5524     const SMDS_MeshNode * node = 0;
5525     if ( myNodes.Length() > 0 )
5526     {
5527       for ( int i = 1; i <= myNodes.Length(); i++ )
5528       {
5529         SMESH_NodeXYZ P2 = myNodes.Value(i);
5530         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5531         {
5532           node = myNodes.Value(i);
5533           break;
5534         }
5535       }
5536     }
5537     else
5538     {
5539       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5540       while(itn->more())
5541       {
5542         SMESH_NodeXYZ P2 = itn->next();
5543         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5544         {
5545           node = P2._node;
5546           break;
5547         }
5548       }
5549     }
5550
5551     if ( !node )
5552       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5553
5554     newNodes.push_back( node );
5555
5556   } // loop on steps
5557
5558   return nbNodes;
5559 }
5560
5561 //=======================================================================
5562 //function : ExtrusParam::makeNodesByNormal2D
5563 //purpose  : create nodes for extrusion using normals of faces
5564 //=======================================================================
5565
5566 int SMESH_MeshEditor::ExtrusParam::
5567 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5568                      const SMDS_MeshNode*              srcNode,
5569                      std::list<const SMDS_MeshNode*> & newNodes,
5570                      const bool                        makeMediumNodes)
5571 {
5572   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5573
5574   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5575
5576   // get normals to faces sharing srcNode
5577   vector< gp_XYZ > norms, baryCenters;
5578   gp_XYZ norm, avgNorm( 0,0,0 );
5579   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5580   while ( faceIt->more() )
5581   {
5582     const SMDS_MeshElement* face = faceIt->next();
5583     if ( myElemsToUse && !myElemsToUse->count( face ))
5584       continue;
5585     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5586     {
5587       norms.push_back( norm );
5588       avgNorm += norm;
5589       if ( !alongAvgNorm )
5590       {
5591         gp_XYZ bc(0,0,0);
5592         int nbN = 0;
5593         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5594           bc += SMESH_NodeXYZ( nIt->next() );
5595         baryCenters.push_back( bc / nbN );
5596       }
5597     }
5598   }
5599
5600   if ( norms.empty() ) return 0;
5601
5602   double normSize = avgNorm.Modulus();
5603   if ( normSize < std::numeric_limits<double>::min() )
5604     return 0;
5605
5606   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5607   {
5608     myDir = avgNorm;
5609     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5610   }
5611
5612   avgNorm /= normSize;
5613
5614   int nbNodes = 0;
5615   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5616   {
5617     gp_XYZ pNew = p;
5618     double stepSize = nextStep();
5619
5620     if ( norms.size() > 1 )
5621     {
5622       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5623       {
5624         // translate plane of a face
5625         baryCenters[ iF ] += norms[ iF ] * stepSize;
5626
5627         // find point of intersection of the face plane located at baryCenters[ iF ]
5628         // and avgNorm located at pNew
5629         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5630         double dot  = ( norms[ iF ] * avgNorm );
5631         if ( dot < std::numeric_limits<double>::min() )
5632           dot = stepSize * 1e-3;
5633         double step = -( norms[ iF ] * pNew + d ) / dot;
5634         pNew += step * avgNorm;
5635       }
5636     }
5637     else
5638     {
5639       pNew += stepSize * avgNorm;
5640     }
5641     p = pNew;
5642
5643     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5644     newNodes.push_back( newNode );
5645   }
5646   return nbNodes;
5647 }
5648
5649 //=======================================================================
5650 //function : ExtrusParam::makeNodesByNormal1D
5651 //purpose  : create nodes for extrusion using normals of edges
5652 //=======================================================================
5653
5654 int SMESH_MeshEditor::ExtrusParam::
5655 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5656                      const SMDS_MeshNode*              srcNode,
5657                      std::list<const SMDS_MeshNode*> & newNodes,
5658                      const bool                        makeMediumNodes)
5659 {
5660   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5661   return 0;
5662 }
5663
5664 //=======================================================================
5665 //function : ExtrusParam::makeNodesAlongTrack
5666 //purpose  : create nodes for extrusion along path
5667 //=======================================================================
5668
5669 int SMESH_MeshEditor::ExtrusParam::
5670 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5671                      const SMDS_MeshNode*              srcNode,
5672                      std::list<const SMDS_MeshNode*> & newNodes,
5673                      const bool                        makeMediumNodes)
5674 {
5675   const Standard_Real aTolAng=1.e-4;
5676
5677   gp_Pnt aV0x = myBaseP;
5678   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5679
5680   const PathPoint& aPP0 = myPathPoints[0];
5681   gp_Pnt aP0x = aPP0.myPnt;
5682   gp_Dir aDT0x= aPP0.myTgt;
5683
5684   std::vector< gp_Pnt > centers;
5685   centers.reserve( NbSteps() * 2 );
5686
5687   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5688
5689   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5690   {
5691     const PathPoint&  aPP  = myPathPoints[j];
5692     const gp_Pnt&     aP1x = aPP.myPnt;
5693     const gp_Dir&    aDT1x = aPP.myTgt;
5694
5695     // Translation
5696     gp_Vec aV01x( aP0x, aP1x );
5697     aTrsf.SetTranslation( aV01x );
5698     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5699     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5700
5701     // rotation 1 [ T1,T0 ]
5702     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5703     if ( fabs( aAngleT1T0 ) > aTolAng )
5704     {
5705       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5706       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5707
5708       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5709     }
5710
5711     // rotation 2
5712     if ( aPP.myAngle != 0. )
5713     {
5714       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5715       aPN1 = aPN1.Transformed( aTrsfRot );
5716     }
5717
5718     // make new node
5719     if ( makeMediumNodes )
5720     {
5721       // create additional node
5722       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5723       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5724       newNodes.push_back( newNode );
5725
5726     }
5727     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5728     newNodes.push_back( newNode );
5729
5730     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5731     centers.push_back( aV1x );
5732
5733     aPN0 = aPN1;
5734     aP0x = aP1x;
5735     aV0x = aV1x;
5736     aDT0x = aDT1x;
5737   }
5738
5739   // scale
5740   if ( !myScales.empty() )
5741   {
5742     gp_Trsf aTrsfScale;
5743     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5744     for ( size_t i = !makeMediumNodes;
5745           i < myScales.size() && node != newNodes.end();
5746           i += ( 1 + !makeMediumNodes ), ++node )
5747     {
5748       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5749       gp_Pnt aN = SMESH_NodeXYZ( *node );
5750       gp_Pnt aP = aN.Transformed( aTrsfScale );
5751       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5752     }
5753   }
5754
5755   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5756 }
5757
5758 //=======================================================================
5759 //function : ExtrusionSweep
5760 //purpose  :
5761 //=======================================================================
5762
5763 SMESH_MeshEditor::PGroupIDs
5764 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5765                                   const gp_Vec&        theStep,
5766                                   const int            theNbSteps,
5767                                   TTElemOfElemListMap& newElemsMap,
5768                                   const int            theFlags,
5769                                   const double         theTolerance)
5770 {
5771   std::list<double> dummy;
5772   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5773                        theFlags, theTolerance );
5774   return ExtrusionSweep( theElems, aParams, newElemsMap );
5775 }
5776
5777 namespace
5778 {
5779
5780 //=======================================================================
5781 //function : getOriFactor
5782 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5783 //           edge curve orientation
5784 //=======================================================================
5785
5786   double getOriFactor( const TopoDS_Edge&   edge,
5787                        const SMDS_MeshNode* n1,
5788                        const SMDS_MeshNode* n2,
5789                        SMESH_MesherHelper&  helper)
5790   {
5791     double u1 = helper.GetNodeU( edge, n1, n2 );
5792     double u2 = helper.GetNodeU( edge, n2, n1 );
5793     return u1 < u2 ? 1. : -1.;
5794   }
5795 }
5796
5797 //=======================================================================
5798 //function : ExtrusionSweep
5799 //purpose  :
5800 //=======================================================================
5801
5802 SMESH_MeshEditor::PGroupIDs
5803 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5804                                   ExtrusParam&         theParams,
5805                                   TTElemOfElemListMap& newElemsMap)
5806 {
5807   ClearLastCreated();
5808
5809   setElemsFirst( theElemSets );
5810   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5811   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5812
5813   // source elements for each generated one
5814   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5815   srcElems.reserve( theElemSets[0].size() );
5816   srcNodes.reserve( theElemSets[1].size() );
5817
5818   const int nbSteps = theParams.NbSteps();
5819   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5820
5821   TNodeOfNodeListMap   mapNewNodes;
5822   TElemOfVecOfNnlmiMap mapElemNewNodes;
5823
5824   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5825                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5826                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5827   // loop on theElems
5828   TIDSortedElemSet::iterator itElem;
5829   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5830   {
5831     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5832     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5833     {
5834       // check element type
5835       const SMDS_MeshElement* elem = *itElem;
5836       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5837         continue;
5838
5839       const size_t nbNodes = elem->NbNodes();
5840       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5841       newNodesItVec.reserve( nbNodes );
5842
5843       // loop on elem nodes
5844       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5845       while ( itN->more() )
5846       {
5847         // check if a node has been already sweeped
5848         const SMDS_MeshNode* node = itN->next();
5849         TNodeOfNodeListMap::iterator nIt =
5850           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5851         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5852         if ( listNewNodes.empty() )
5853         {
5854           // make new nodes
5855
5856           // check if we are to create medium nodes between corner ones
5857           bool needMediumNodes = false;
5858           if ( isQuadraticMesh )
5859           {
5860             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5861             while (it->more() && !needMediumNodes )
5862             {
5863               const SMDS_MeshElement* invElem = it->next();
5864               if ( invElem != elem && !theElems.count( invElem )) continue;
5865               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5866               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5867                 needMediumNodes = true;
5868             }
5869           }
5870           // create nodes for all steps
5871           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5872           {
5873             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5874             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5875             {
5876               myLastCreatedNodes.push_back( *newNodesIt );
5877               srcNodes.push_back( node );
5878             }
5879           }
5880           else
5881           {
5882             if ( theParams.ToMakeBoundary() )
5883             {
5884               GetMeshDS()->Modified();
5885               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5886             }
5887             break; // newNodesItVec will be shorter than nbNodes
5888           }
5889         }
5890         newNodesItVec.push_back( nIt );
5891       }
5892       // make new elements
5893       if ( newNodesItVec.size() == nbNodes )
5894         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5895     }
5896   }
5897
5898   if ( theParams.ToMakeBoundary() ) {
5899     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5900   }
5901   PGroupIDs newGroupIDs;
5902   if ( theParams.ToMakeGroups() )
5903     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5904
5905   return newGroupIDs;
5906 }
5907
5908 //=======================================================================
5909 //function : ExtrusionAlongTrack
5910 //purpose  :
5911 //=======================================================================
5912 SMESH_MeshEditor::Extrusion_Error
5913 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5914                                        SMESH_Mesh*          theTrackMesh,
5915                                        SMDS_ElemIteratorPtr theTrackIterator,
5916                                        const SMDS_MeshNode* theN1,
5917                                        std::list<double>&   theAngles,
5918                                        const bool           theAngleVariation,
5919                                        std::list<double>&   theScales,
5920                                        const bool           theScaleVariation,
5921                                        const gp_Pnt*        theRefPoint,
5922                                        const bool           theMakeGroups)
5923 {
5924   ClearLastCreated();
5925
5926   // 1. Check data
5927   if ( theElements[0].empty() && theElements[1].empty() )
5928     return EXTR_NO_ELEMENTS;
5929
5930   ASSERT( theTrackMesh );
5931   if ( ! theTrackIterator || !theTrackIterator->more() )
5932     return EXTR_NO_ELEMENTS;
5933
5934   // 2. Get ordered nodes
5935   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5936   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5937   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5938   if ( branchEdges.empty() )
5939     return EXTR_PATH_NOT_EDGE;
5940
5941   if ( branchEdges.size() > 1 )
5942     return EXTR_BAD_PATH_SHAPE;
5943
5944   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5945   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5946   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5947     return EXTR_BAD_STARTING_NODE;
5948
5949   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5950   {
5951     // add medium nodes to pathNodes
5952     std::vector< const SMDS_MeshNode* >    pathNodes2;
5953     std::vector< const SMDS_MeshElement* > pathEdges2;
5954     pathNodes2.reserve( pathNodes.size() * 2 );
5955     pathEdges2.reserve( pathEdges.size() * 2 );
5956     for ( size_t i = 0; i < pathEdges.size(); ++i )
5957     {
5958       pathNodes2.push_back( pathNodes[i] );
5959       pathEdges2.push_back( pathEdges[i] );
5960       if ( pathEdges[i]->IsQuadratic() )
5961       {
5962         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5963         pathEdges2.push_back( pathEdges[i] );
5964       }
5965     }
5966     pathNodes2.push_back( pathNodes.back() );
5967     pathEdges.swap( pathEdges2 );
5968     pathNodes.swap( pathNodes2 );
5969   }
5970
5971   // 3. Get path data at pathNodes
5972
5973   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5974
5975   if ( theAngleVariation )
5976     linearAngleVariation( points.size()-1, theAngles );
5977   if ( theScaleVariation )
5978     linearScaleVariation( points.size()-1, theScales );
5979
5980   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5981   std::list<double>::iterator angle = theAngles.begin();
5982
5983   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5984
5985   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5986   std::map< int, double >::iterator id2factor;
5987   SMESH_MesherHelper pathHelper( *theTrackMesh );
5988   gp_Pnt p; gp_Vec tangent;
5989   const double tol2 = gp::Resolution() * gp::Resolution();
5990
5991   for ( size_t i = 0; i < pathNodes.size(); ++i )
5992   {
5993     ExtrusParam::PathPoint & point = points[ i ];
5994
5995     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5996
5997     if ( angle != theAngles.end() )
5998       point.myAngle = *angle++;
5999
6000     tangent.SetCoord( 0,0,0 );
6001     const int          shapeID = pathNodes[ i ]->GetShapeID();
6002     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6003     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6004     switch ( shapeType )
6005     {
6006     case TopAbs_EDGE:
6007     {
6008       TopoDS_Edge edge = TopoDS::Edge( shape );
6009       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6010       if ( id2factor->second == 0 )
6011       {
6012         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6013         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6014       }
6015       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6016       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6017       curve->D1( u, p, tangent );
6018       tangent *= id2factor->second;
6019       break;
6020     }
6021     case TopAbs_VERTEX:
6022     {
6023       int nbEdges = 0;
6024       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6025       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6026       {
6027         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6028         for ( int di = -1; di <= 0; ++di )
6029         {
6030           size_t j = i + di;
6031           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6032           {
6033             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6034             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6035             if ( id2factor->second == 0 )
6036             {
6037               if ( j < i )
6038                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6039               else
6040                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6041             }
6042             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6043             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6044             gp_Vec du;
6045             curve->D1( u, p, du );
6046             double size2 = du.SquareMagnitude();
6047             if ( du.SquareMagnitude() > tol2 )
6048             {
6049               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6050               nbEdges++;
6051             }
6052             break;
6053           }
6054         }
6055       }
6056       if ( nbEdges > 0 )
6057         break;
6058     }
6059     default:
6060     {
6061       for ( int di = -1; di <= 1; di += 2 )
6062       {
6063         size_t j = i + di;
6064         if ( j < pathNodes.size() )
6065         {
6066           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6067           double size2 = dir.SquareMagnitude();
6068           if ( size2 > tol2 )
6069             tangent += dir.Divided( Sqrt( size2 )) * di;
6070         }
6071       }
6072     }
6073     } // switch ( shapeType )
6074
6075     if ( tangent.SquareMagnitude() < tol2 )
6076       return EXTR_CANT_GET_TANGENT;
6077
6078     point.myTgt = tangent;
6079
6080   } // loop on pathNodes
6081
6082
6083   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6084   TTElemOfElemListMap newElemsMap;
6085
6086   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6087
6088   return EXTR_OK;
6089 }
6090
6091 //=======================================================================
6092 //function : linearAngleVariation
6093 //purpose  : spread values over nbSteps
6094 //=======================================================================
6095
6096 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6097                                             list<double>& Angles)
6098 {
6099   int nbAngles = Angles.size();
6100   if( nbSteps > nbAngles && nbAngles > 0 )
6101   {
6102     vector<double> theAngles(nbAngles);
6103     theAngles.assign( Angles.begin(), Angles.end() );
6104
6105     list<double> res;
6106     double rAn2St = double( nbAngles ) / double( nbSteps );
6107     double angPrev = 0, angle;
6108     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6109     {
6110       double angCur = rAn2St * ( iSt+1 );
6111       double angCurFloor  = floor( angCur );
6112       double angPrevFloor = floor( angPrev );
6113       if ( angPrevFloor == angCurFloor )
6114         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6115       else {
6116         int iP = int( angPrevFloor );
6117         double angPrevCeil = ceil(angPrev);
6118         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6119
6120         int iC = int( angCurFloor );
6121         if ( iC < nbAngles )
6122           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6123
6124         iP = int( angPrevCeil );
6125         while ( iC-- > iP )
6126           angle += theAngles[ iC ];
6127       }
6128       res.push_back(angle);
6129       angPrev = angCur;
6130     }
6131     Angles.swap( res );
6132   }
6133 }
6134
6135 //=======================================================================
6136 //function : linearScaleVariation
6137 //purpose  : spread values over nbSteps 
6138 //=======================================================================
6139
6140 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6141                                             std::list<double>& theScales)
6142 {
6143   int nbScales = theScales.size();
6144   std::vector<double> myScales;
6145   myScales.reserve( theNbSteps );
6146   std::list<double>::const_iterator scale = theScales.begin();
6147   double prevScale = 1.0;
6148   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6149   {
6150     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6151     int    stDelta = Max( 1, iStep - myScales.size());
6152     double scDelta = ( *scale - prevScale ) / stDelta;
6153     for ( int iStep = 0; iStep < stDelta; ++iStep )
6154     {
6155       myScales.push_back( prevScale + scDelta );
6156       prevScale = myScales.back();
6157     }
6158     prevScale = *scale;
6159   }
6160   theScales.assign( myScales.begin(), myScales.end() );
6161 }
6162
6163 //================================================================================
6164 /*!
6165  * \brief Move or copy theElements applying theTrsf to their nodes
6166  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6167  *  \param theTrsf - transformation to apply
6168  *  \param theCopy - if true, create translated copies of theElems
6169  *  \param theMakeGroups - if true and theCopy, create translated groups
6170  *  \param theTargetMesh - mesh to copy translated elements into
6171  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6172  */
6173 //================================================================================
6174
6175 SMESH_MeshEditor::PGroupIDs
6176 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6177                              const gp_Trsf&     theTrsf,
6178                              const bool         theCopy,
6179                              const bool         theMakeGroups,
6180                              SMESH_Mesh*        theTargetMesh)
6181 {
6182   ClearLastCreated();
6183   myLastCreatedElems.reserve( theElems.size() );
6184
6185   bool needReverse = false;
6186   string groupPostfix;
6187   switch ( theTrsf.Form() ) {
6188   case gp_PntMirror:
6189     needReverse = true;
6190     groupPostfix = "mirrored";
6191     break;
6192   case gp_Ax1Mirror:
6193     groupPostfix = "mirrored";
6194     break;
6195   case gp_Ax2Mirror:
6196     needReverse = true;
6197     groupPostfix = "mirrored";
6198     break;
6199   case gp_Rotation:
6200     groupPostfix = "rotated";
6201     break;
6202   case gp_Translation:
6203     groupPostfix = "translated";
6204     break;
6205   case gp_Scale:
6206     groupPostfix = "scaled";
6207     break;
6208   case gp_CompoundTrsf: // different scale by axis
6209     groupPostfix = "scaled";
6210     break;
6211   default:
6212     needReverse = false;
6213     groupPostfix = "transformed";
6214   }
6215
6216   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6217   SMESHDS_Mesh* aMesh    = GetMeshDS();
6218
6219   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6220   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6221   SMESH_MeshEditor::ElemFeatures elemType;
6222
6223   // map old node to new one
6224   TNodeNodeMap nodeMap;
6225
6226   // elements sharing moved nodes; those of them which have all
6227   // nodes mirrored but are not in theElems are to be reversed
6228   TIDSortedElemSet inverseElemSet;
6229
6230   // source elements for each generated one
6231   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6232
6233   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6234   TIDSortedElemSet orphanNode;
6235
6236   if ( theElems.empty() ) // transform the whole mesh
6237   {
6238     // add all elements
6239     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6240     while ( eIt->more() ) theElems.insert( eIt->next() );
6241     // add orphan nodes
6242     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6243     while ( nIt->more() )
6244     {
6245       const SMDS_MeshNode* node = nIt->next();
6246       if ( node->NbInverseElements() == 0)
6247         orphanNode.insert( node );
6248     }
6249   }
6250
6251   // loop on elements to transform nodes : first orphan nodes then elems
6252   TIDSortedElemSet::iterator itElem;
6253   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6254   for (int i=0; i<2; i++)
6255     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6256     {
6257       const SMDS_MeshElement* elem = *itElem;
6258       if ( !elem )
6259         continue;
6260
6261       // loop on elem nodes
6262       double coord[3];
6263       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6264       while ( itN->more() )
6265       {
6266         const SMDS_MeshNode* node = cast2Node( itN->next() );
6267         // check if a node has been already transformed
6268         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6269           nodeMap.insert( make_pair ( node, node ));
6270         if ( !n2n_isnew.second )
6271           continue;
6272
6273         node->GetXYZ( coord );
6274         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6275         if ( theTargetMesh ) {
6276           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6277           n2n_isnew.first->second = newNode;
6278           myLastCreatedNodes.push_back(newNode);
6279           srcNodes.push_back( node );
6280         }
6281         else if ( theCopy ) {
6282           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6283           n2n_isnew.first->second = newNode;
6284           myLastCreatedNodes.push_back(newNode);
6285           srcNodes.push_back( node );
6286         }
6287         else {
6288           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6289           // node position on shape becomes invalid
6290           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6291             ( SMDS_SpacePosition::originSpacePosition() );
6292         }
6293
6294         // keep inverse elements
6295         if ( !theCopy && !theTargetMesh && needReverse ) {
6296           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6297           while ( invElemIt->more() ) {
6298             const SMDS_MeshElement* iel = invElemIt->next();
6299             inverseElemSet.insert( iel );
6300           }
6301         }
6302       }
6303     } // loop on elems in { &orphanNode, &theElems };
6304
6305   // either create new elements or reverse mirrored ones
6306   if ( !theCopy && !needReverse && !theTargetMesh )
6307     return PGroupIDs();
6308
6309   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6310
6311   // Replicate or reverse elements
6312
6313   std::vector<int> iForw;
6314   vector<const SMDS_MeshNode*> nodes;
6315   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6316   {
6317     const SMDS_MeshElement* elem = *itElem;
6318     if ( !elem ) continue;
6319
6320     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6321     size_t               nbNodes  = elem->NbNodes();
6322     if ( geomType == SMDSGeom_NONE ) continue; // node
6323
6324     nodes.resize( nbNodes );
6325
6326     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6327     {
6328       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6329       if ( !aPolyedre )
6330         continue;
6331       nodes.clear();
6332       bool allTransformed = true;
6333       int nbFaces = aPolyedre->NbFaces();
6334       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6335       {
6336         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6337         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6338         {
6339           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6340           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6341           if ( nodeMapIt == nodeMap.end() )
6342             allTransformed = false; // not all nodes transformed
6343           else
6344             nodes.push_back((*nodeMapIt).second);
6345         }
6346         if ( needReverse && allTransformed )
6347           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6348       }
6349       if ( !allTransformed )
6350         continue; // not all nodes transformed
6351     }
6352     else // ----------------------- the rest element types
6353     {
6354       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6355       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6356       const vector<int>&    i = needReverse ? iRev : iForw;
6357
6358       // find transformed nodes
6359       size_t iNode = 0;
6360       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6361       while ( itN->more() ) {
6362         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6363         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6364         if ( nodeMapIt == nodeMap.end() )
6365           break; // not all nodes transformed
6366         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6367       }
6368       if ( iNode != nbNodes )
6369         continue; // not all nodes transformed
6370     }
6371
6372     if ( editor ) {
6373       // copy in this or a new mesh
6374       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6375         srcElems.push_back( elem );
6376     }
6377     else {
6378       // reverse element as it was reversed by transformation
6379       if ( nbNodes > 2 )
6380         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6381     }
6382
6383   } // loop on elements
6384
6385   if ( editor && editor != this )
6386     myLastCreatedElems.swap( editor->myLastCreatedElems );
6387
6388   PGroupIDs newGroupIDs;
6389
6390   if ( ( theMakeGroups && theCopy ) ||
6391        ( theMakeGroups && theTargetMesh ) )
6392     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6393
6394   return newGroupIDs;
6395 }
6396
6397 //================================================================================
6398 /*!
6399  * \brief Make an offset mesh from a source 2D mesh
6400  *  \param [in] theElements - source faces
6401  *  \param [in] theValue - offset value
6402  *  \param [out] theTgtMesh - a mesh to add offset elements to
6403  *  \param [in] theMakeGroups - to generate groups
6404  *  \return PGroupIDs - IDs of created groups. NULL means failure
6405  */
6406 //================================================================================
6407
6408 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6409                                                       const double       theValue,
6410                                                       SMESH_Mesh*        theTgtMesh,
6411                                                       const bool         theMakeGroups,
6412                                                       const bool         theCopyElements,
6413                                                       const bool         theFixSelfIntersection)
6414 {
6415   SMESHDS_Mesh*    meshDS = GetMeshDS();
6416   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6417   SMESH_MeshEditor tgtEditor( theTgtMesh );
6418
6419   SMDS_ElemIteratorPtr eIt;
6420   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6421   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6422
6423   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6424   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6425   std::unique_ptr< SMDS_Mesh > offsetMesh
6426     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6427                                    theFixSelfIntersection,
6428                                    new2OldFaces, new2OldNodes ));
6429   if ( offsetMesh->NbElements() == 0 )
6430     return PGroupIDs(); // MakeOffset() failed
6431
6432
6433   if ( theTgtMesh == myMesh && !theCopyElements )
6434   {
6435     // clear the source elements
6436     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6437     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6438     while ( eIt->more() )
6439       meshDS->RemoveFreeElement( eIt->next(), 0 );
6440   }
6441
6442   // offsetMesh->Modified();
6443   // offsetMesh->CompactMesh(); // make IDs start from 1
6444
6445   // source elements for each generated one
6446   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6447   srcElems.reserve( new2OldFaces.size() );
6448   srcNodes.reserve( new2OldNodes.size() );
6449
6450   ClearLastCreated();
6451   myLastCreatedElems.reserve( new2OldFaces.size() );
6452   myLastCreatedNodes.reserve( new2OldNodes.size() );
6453
6454   // copy offsetMesh to theTgtMesh
6455
6456   int idShift = meshDS->MaxNodeID();
6457   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6458     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6459     {
6460 #ifndef _DEBUG_
6461       if ( n->NbInverseElements() > 0 )
6462 #endif
6463       {
6464         const SMDS_MeshNode* n2 =
6465           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6466         myLastCreatedNodes.push_back( n2 );
6467         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6468       }
6469     }
6470
6471   ElemFeatures elemType;
6472   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6473     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6474     {
6475       elemType.Init( f );
6476       elemType.myNodes.clear();
6477       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6478       {
6479         const SMDS_MeshNode* n2 = nIt->next();
6480         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6481       }
6482       tgtEditor.AddElement( elemType.myNodes, elemType );
6483       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6484     }
6485
6486   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6487
6488   PGroupIDs newGroupIDs;
6489   if ( theMakeGroups )
6490     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6491   else
6492     newGroupIDs.reset( new std::list< int > );
6493
6494   return newGroupIDs;
6495 }
6496
6497 //=======================================================================
6498 /*!
6499  * \brief Create groups of elements made during transformation
6500  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6501  *  \param elemGens - elements making corresponding myLastCreatedElems
6502  *  \param postfix - to push_back to names of new groups
6503  *  \param targetMesh - mesh to create groups in
6504  *  \param topPresent - is there are "top" elements that are created by sweeping
6505  */
6506 //=======================================================================
6507
6508 SMESH_MeshEditor::PGroupIDs
6509 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6510                                  const SMESH_SequenceOfElemPtr& elemGens,
6511                                  const std::string&             postfix,
6512                                  SMESH_Mesh*                    targetMesh,
6513                                  const bool                     topPresent)
6514 {
6515   PGroupIDs newGroupIDs( new list<int> );
6516   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6517
6518   // Sort existing groups by types and collect their names
6519
6520   // containers to store an old group and generated new ones;
6521   // 1st new group is for result elems of different type than a source one;
6522   // 2nd new group is for same type result elems ("top" group at extrusion)
6523   using boost::tuple;
6524   using boost::make_tuple;
6525   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6526   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6527   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6528   // group names
6529   set< string > groupNames;
6530
6531   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6532   if ( !groupIt->more() ) return newGroupIDs;
6533
6534   int newGroupID = mesh->GetGroupIds().back()+1;
6535   while ( groupIt->more() )
6536   {
6537     SMESH_Group * group = groupIt->next();
6538     if ( !group ) continue;
6539     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6540     if ( !groupDS || groupDS->IsEmpty() ) continue;
6541     groupNames.insert    ( group->GetName() );
6542     groupDS->SetStoreName( group->GetName() );
6543     const SMDSAbs_ElementType type = groupDS->GetType();
6544     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6545     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6546     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6547     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6548   }
6549
6550   // Loop on nodes and elements to add them in new groups
6551
6552   vector< const SMDS_MeshElement* > resultElems;
6553   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6554   {
6555     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6556     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6557     if ( gens.size() != elems.size() )
6558       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6559
6560     // loop on created elements
6561     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6562     {
6563       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6564       if ( !sourceElem ) {
6565         MESSAGE("generateGroups(): NULL source element");
6566         continue;
6567       }
6568       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6569       if ( groupsOldNew.empty() ) { // no groups of this type at all
6570         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6571           ++iElem; // skip all elements made by sourceElem
6572         continue;
6573       }
6574       // collect all elements made by the iElem-th sourceElem
6575       resultElems.clear();
6576       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6577         if ( resElem != sourceElem )
6578           resultElems.push_back( resElem );
6579       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6580         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6581           if ( resElem != sourceElem )
6582             resultElems.push_back( resElem );
6583
6584       const SMDS_MeshElement* topElem = 0;
6585       if ( isNodes ) // there must be a top element
6586       {
6587         topElem = resultElems.back();
6588         resultElems.pop_back();
6589       }
6590       else
6591       {
6592         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6593         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6594           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6595           {
6596             topElem = *resElemIt;
6597             *resElemIt = 0; // erase *resElemIt
6598             break;
6599           }
6600       }
6601       // add resultElems to groups originted from ones the sourceElem belongs to
6602       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6603       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6604       {
6605         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6606         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6607         {
6608           // fill in a new group
6609           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6610           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6611           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6612             if ( *resElemIt )
6613               newGroup.Add( *resElemIt );
6614
6615           // fill a "top" group
6616           if ( topElem )
6617           {
6618             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6619             newTopGroup.Add( topElem );
6620           }
6621         }
6622       }
6623     } // loop on created elements
6624   }// loop on nodes and elements
6625
6626   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6627
6628   list<int> topGrouIds;
6629   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6630   {
6631     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6632     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6633                                       orderedOldNewGroups[i]->get<2>() };
6634     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6635     {
6636       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6637       if ( newGroupDS->IsEmpty() )
6638       {
6639         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6640       }
6641       else
6642       {
6643         // set group type
6644         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6645
6646         // make a name
6647         const bool isTop = ( topPresent &&
6648                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6649                              is2nd );
6650
6651         string name = oldGroupDS->GetStoreName();
6652         { // remove trailing whitespaces (issue 22599)
6653           size_t size = name.size();
6654           while ( size > 1 && isspace( name[ size-1 ]))
6655             --size;
6656           if ( size != name.size() )
6657           {
6658             name.resize( size );
6659             oldGroupDS->SetStoreName( name.c_str() );
6660           }
6661         }
6662         if ( !targetMesh ) {
6663           string suffix = ( isTop ? "top": postfix.c_str() );
6664           name += "_";
6665           name += suffix;
6666           int nb = 1;
6667           while ( !groupNames.insert( name ).second ) // name exists
6668             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6669         }
6670         else if ( isTop ) {
6671           name += "_top";
6672         }
6673         newGroupDS->SetStoreName( name.c_str() );
6674
6675         // make a SMESH_Groups
6676         mesh->AddGroup( newGroupDS );
6677         if ( isTop )
6678           topGrouIds.push_back( newGroupDS->GetID() );
6679         else
6680           newGroupIDs->push_back( newGroupDS->GetID() );
6681       }
6682     }
6683   }
6684   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6685
6686   return newGroupIDs;
6687 }
6688
6689 //================================================================================
6690 /*!
6691  *  * \brief Return list of group of nodes close to each other within theTolerance
6692  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6693  *  *        an Octree algorithm
6694  *  \param [in,out] theNodes - the nodes to treat
6695  *  \param [in]     theTolerance - the tolerance
6696  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6697  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6698  *         corner and medium nodes in separate groups
6699  */
6700 //================================================================================
6701
6702 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6703                                             const double         theTolerance,
6704                                             TListOfListOfNodes & theGroupsOfNodes,
6705                                             bool                 theSeparateCornersAndMedium)
6706 {
6707   ClearLastCreated();
6708
6709   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6710        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6711        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6712     theSeparateCornersAndMedium = false;
6713
6714   TIDSortedNodeSet& corners = theNodes;
6715   TIDSortedNodeSet  medium;
6716
6717   if ( theNodes.empty() ) // get all nodes in the mesh
6718   {
6719     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6720     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6721     if ( theSeparateCornersAndMedium )
6722       while ( nIt->more() )
6723       {
6724         const SMDS_MeshNode* n = nIt->next();
6725         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6726         nodeSet->insert( nodeSet->end(), n );
6727       }
6728     else
6729       while ( nIt->more() )
6730         theNodes.insert( theNodes.end(), nIt->next() );
6731   }
6732   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6733   {
6734     TIDSortedNodeSet::iterator nIt = corners.begin();
6735     while ( nIt != corners.end() )
6736       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6737       {
6738         medium.insert( medium.end(), *nIt );
6739         corners.erase( nIt++ );
6740       }
6741       else
6742       {
6743         ++nIt;
6744       }
6745   }
6746
6747   if ( !corners.empty() )
6748     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6749   if ( !medium.empty() )
6750     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6751 }
6752
6753 //=======================================================================
6754 //function : SimplifyFace
6755 //purpose  : split a chain of nodes into several closed chains
6756 //=======================================================================
6757
6758 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6759                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6760                                     vector<int>&                         quantities) const
6761 {
6762   int nbNodes = faceNodes.size();
6763   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6764     --nbNodes;
6765   if ( nbNodes < 3 )
6766     return 0;
6767   size_t prevNbQuant = quantities.size();
6768
6769   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6770   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6771   map< const SMDS_MeshNode*, int >::iterator nInd;
6772
6773   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6774   simpleNodes.push_back( faceNodes[0] );
6775   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6776   {
6777     if ( faceNodes[ iCur ] != simpleNodes.back() )
6778     {
6779       int index = simpleNodes.size();
6780       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6781       int prevIndex = nInd->second;
6782       if ( prevIndex < index )
6783       {
6784         // a sub-loop found
6785         int loopLen = index - prevIndex;
6786         if ( loopLen > 2 )
6787         {
6788           // store the sub-loop
6789           quantities.push_back( loopLen );
6790           for ( int i = prevIndex; i < index; i++ )
6791             poly_nodes.push_back( simpleNodes[ i ]);
6792         }
6793         simpleNodes.resize( prevIndex+1 );
6794       }
6795       else
6796       {
6797         simpleNodes.push_back( faceNodes[ iCur ]);
6798       }
6799     }
6800   }
6801
6802   if ( simpleNodes.size() > 2 )
6803   {
6804     quantities.push_back( simpleNodes.size() );
6805     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6806   }
6807
6808   return quantities.size() - prevNbQuant;
6809 }
6810
6811 //=======================================================================
6812 //function : MergeNodes
6813 //purpose  : In each group, the cdr of nodes are substituted by the first one
6814 //           in all elements.
6815 //=======================================================================
6816
6817 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6818                                    const bool           theAvoidMakingHoles)
6819 {
6820   ClearLastCreated();
6821
6822   SMESHDS_Mesh* mesh = GetMeshDS();
6823
6824   TNodeNodeMap nodeNodeMap; // node to replace - new node
6825   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6826   list< int > rmElemIds, rmNodeIds;
6827   vector< ElemFeatures > newElemDefs;
6828
6829   // Fill nodeNodeMap and elems
6830
6831   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6832   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6833   {
6834     list<const SMDS_MeshNode*>& nodes = *grIt;
6835     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6836     const SMDS_MeshNode* nToKeep = *nIt;
6837     for ( ++nIt; nIt != nodes.end(); nIt++ )
6838     {
6839       const SMDS_MeshNode* nToRemove = *nIt;
6840       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6841       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6842       while ( invElemIt->more() ) {
6843         const SMDS_MeshElement* elem = invElemIt->next();
6844         elems.insert(elem);
6845       }
6846     }
6847   }
6848
6849   // Apply recursive replacements (BUG 0020185)
6850   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6851   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6852   {
6853     const SMDS_MeshNode* nToKeep = nnIt->second;
6854     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6855     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6856     {
6857       nToKeep = nnIt_i->second;
6858       nnIt->second = nToKeep;
6859       nnIt_i = nodeNodeMap.find( nToKeep );
6860     }
6861   }
6862
6863   if ( theAvoidMakingHoles )
6864   {
6865     // find elements whose topology changes
6866
6867     vector<const SMDS_MeshElement*> pbElems;
6868     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6869     for ( ; eIt != elems.end(); ++eIt )
6870     {
6871       const SMDS_MeshElement* elem = *eIt;
6872       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6873       while ( itN->more() )
6874       {
6875         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6876         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6877         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6878         {
6879           // several nodes of elem stick
6880           pbElems.push_back( elem );
6881           break;
6882         }
6883       }
6884     }
6885     // exclude from merge nodes causing spoiling element
6886     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6887     {
6888       bool nodesExcluded = false;
6889       for ( size_t i = 0; i < pbElems.size(); ++i )
6890       {
6891         size_t prevNbMergeNodes = nodeNodeMap.size();
6892         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6893              prevNbMergeNodes < nodeNodeMap.size() )
6894           nodesExcluded = true;
6895       }
6896       if ( !nodesExcluded )
6897         break;
6898     }
6899   }
6900
6901   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6902   {
6903     const SMDS_MeshNode* nToRemove = nnIt->first;
6904     const SMDS_MeshNode* nToKeep   = nnIt->second;
6905     if ( nToRemove != nToKeep )
6906     {
6907       rmNodeIds.push_back( nToRemove->GetID() );
6908       AddToSameGroups( nToKeep, nToRemove, mesh );
6909       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6910       // w/o creating node in place of merged ones.
6911       SMDS_PositionPtr pos = nToRemove->GetPosition();
6912       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6913         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6914           sm->SetIsAlwaysComputed( true );
6915     }
6916   }
6917
6918   // Change element nodes or remove an element
6919
6920   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6921   for ( ; eIt != elems.end(); eIt++ )
6922   {
6923     const SMDS_MeshElement* elem = *eIt;
6924     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6925
6926     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6927     if ( !keepElem )
6928       rmElemIds.push_back( elem->GetID() );
6929
6930     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6931     {
6932       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6933                                                & newElemDefs[i].myNodes[0],
6934                                                newElemDefs[i].myNodes.size() ))
6935       {
6936         if ( i == 0 )
6937         {
6938           newElemDefs[i].SetID( elem->GetID() );
6939           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6940           if ( !keepElem ) rmElemIds.pop_back();
6941         }
6942         else
6943         {
6944           newElemDefs[i].SetID( -1 );
6945         }
6946         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6947         if ( sm && newElem )
6948           sm->AddElement( newElem );
6949         if ( elem != newElem )
6950           ReplaceElemInGroups( elem, newElem, mesh );
6951       }
6952     }
6953   }
6954
6955   // Remove bad elements, then equal nodes (order important)
6956   Remove( rmElemIds, /*isNodes=*/false );
6957   Remove( rmNodeIds, /*isNodes=*/true );
6958
6959   return;
6960 }
6961
6962 //=======================================================================
6963 //function : applyMerge
6964 //purpose  : Compute new connectivity of an element after merging nodes
6965 //  \param [in] elems - the element
6966 //  \param [out] newElemDefs - definition(s) of result element(s)
6967 //  \param [inout] nodeNodeMap - nodes to merge
6968 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6969 //              after merging (but not degenerated), removes nodes causing
6970 //              the invalidity from \a nodeNodeMap.
6971 //  \return bool - true if the element should be removed
6972 //=======================================================================
6973
6974 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6975                                    vector< ElemFeatures >& newElemDefs,
6976                                    TNodeNodeMap&           nodeNodeMap,
6977                                    const bool              avoidMakingHoles )
6978 {
6979   bool toRemove = false; // to remove elem
6980   int nbResElems = 1;    // nb new elements
6981
6982   newElemDefs.resize(nbResElems);
6983   newElemDefs[0].Init( elem );
6984   newElemDefs[0].myNodes.clear();
6985
6986   set<const SMDS_MeshNode*> nodeSet;
6987   vector< const SMDS_MeshNode*>   curNodes;
6988   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6989   vector<int> iRepl;
6990
6991   const        int  nbNodes = elem->NbNodes();
6992   SMDSAbs_EntityType entity = elem->GetEntityType();
6993
6994   curNodes.resize( nbNodes );
6995   uniqueNodes.resize( nbNodes );
6996   iRepl.resize( nbNodes );
6997   int iUnique = 0, iCur = 0, nbRepl = 0;
6998
6999   // Get new seq of nodes
7000
7001   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7002   while ( itN->more() )
7003   {
7004     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7005
7006     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7007     if ( nnIt != nodeNodeMap.end() ) {
7008       n = (*nnIt).second;
7009     }
7010     curNodes[ iCur ] = n;
7011     bool isUnique = nodeSet.insert( n ).second;
7012     if ( isUnique )
7013       uniqueNodes[ iUnique++ ] = n;
7014     else
7015       iRepl[ nbRepl++ ] = iCur;
7016     iCur++;
7017   }
7018
7019   // Analyse element topology after replacement
7020
7021   int nbUniqueNodes = nodeSet.size();
7022   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7023   {
7024     toRemove = true;
7025     nbResElems = 0;
7026
7027     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7028     {
7029       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7030       int nbCorners = nbNodes / 2;
7031       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7032       {
7033         int iNext = ( iCur + 1 ) % nbCorners;
7034         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7035         {
7036           int iMedium = iCur + nbCorners;
7037           vector< const SMDS_MeshNode* >::iterator i =
7038             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7039                        uniqueNodes.end(),
7040                        curNodes[ iMedium ]);
7041           if ( i != uniqueNodes.end() )
7042           {
7043             --nbUniqueNodes;
7044             for ( ; i+1 != uniqueNodes.end(); ++i )
7045               *i = *(i+1);
7046           }
7047         }
7048       }
7049     }
7050
7051     switch ( entity )
7052     {
7053     case SMDSEntity_Polygon:
7054     case SMDSEntity_Quad_Polygon: // Polygon
7055     {
7056       ElemFeatures* elemType = & newElemDefs[0];
7057       const bool isQuad = elemType->myIsQuad;
7058       if ( isQuad )
7059         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7060           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7061
7062       // a polygon can divide into several elements
7063       vector<const SMDS_MeshNode *> polygons_nodes;
7064       vector<int> quantities;
7065       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7066       newElemDefs.resize( nbResElems );
7067       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7068       {
7069         ElemFeatures* elemType = & newElemDefs[iface];
7070         if ( iface ) elemType->Init( elem );
7071
7072         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7073         int nbNewNodes = quantities[iface];
7074         face_nodes.assign( polygons_nodes.begin() + inode,
7075                            polygons_nodes.begin() + inode + nbNewNodes );
7076         inode += nbNewNodes;
7077         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7078         {
7079           bool isValid = ( nbNewNodes % 2 == 0 );
7080           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7081             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7082           elemType->SetQuad( isValid );
7083           if ( isValid ) // put medium nodes after corners
7084             SMDS_MeshCell::applyInterlaceRev
7085               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7086                                                     nbNewNodes ), face_nodes );
7087         }
7088         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7089       }
7090       nbUniqueNodes = newElemDefs[0].myNodes.size();
7091       break;
7092     } // Polygon
7093
7094     case SMDSEntity_Polyhedra: // Polyhedral volume
7095     {
7096       if ( nbUniqueNodes >= 4 )
7097       {
7098         // each face has to be analyzed in order to check volume validity
7099         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7100         {
7101           int nbFaces = aPolyedre->NbFaces();
7102
7103           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7104           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7105           vector<const SMDS_MeshNode *>  faceNodes;
7106           poly_nodes.clear();
7107           quantities.clear();
7108
7109           for (int iface = 1; iface <= nbFaces; iface++)
7110           {
7111             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7112             faceNodes.resize( nbFaceNodes );
7113             for (int inode = 1; inode <= nbFaceNodes; inode++)
7114             {
7115               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7116               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7117               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7118                 faceNode = (*nnIt).second;
7119               faceNodes[inode - 1] = faceNode;
7120             }
7121             SimplifyFace(faceNodes, poly_nodes, quantities);
7122           }
7123
7124           if ( quantities.size() > 3 )
7125           {
7126             // TODO: remove coincident faces
7127             nbResElems = 1;
7128             nbUniqueNodes = newElemDefs[0].myNodes.size();
7129           }
7130         }
7131       }
7132     }
7133     break;
7134
7135     // Regular elements
7136     // TODO not all the possible cases are solved. Find something more generic?
7137     case SMDSEntity_Edge: //////// EDGE
7138     case SMDSEntity_Triangle: //// TRIANGLE
7139     case SMDSEntity_Quad_Triangle:
7140     case SMDSEntity_Tetra:
7141     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7142     {
7143       break;
7144     }
7145     case SMDSEntity_Quad_Edge:
7146     {
7147       break;
7148     }
7149     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7150     {
7151       if ( nbUniqueNodes < 3 )
7152         toRemove = true;
7153       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7154         toRemove = true; // opposite nodes stick
7155       else
7156         toRemove = false;
7157       break;
7158     }
7159     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7160     {
7161       //   1    5    2
7162       //    +---+---+
7163       //    |       |
7164       //   4+       +6
7165       //    |       |
7166       //    +---+---+
7167       //   0    7    3
7168       if ( nbUniqueNodes == 6 &&
7169            iRepl[0] < 4       &&
7170            ( nbRepl == 1 || iRepl[1] >= 4 ))
7171       {
7172         toRemove = false;
7173       }
7174       break;
7175     }
7176     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7177     {
7178       //   1    5    2
7179       //    +---+---+
7180       //    |       |
7181       //   4+  8+   +6
7182       //    |       |
7183       //    +---+---+
7184       //   0    7    3
7185       if ( nbUniqueNodes == 7 &&
7186            iRepl[0] < 4       &&
7187            ( nbRepl == 1 || iRepl[1] != 8 ))
7188       {
7189         toRemove = false;
7190       }
7191       break;
7192     }
7193     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7194     {
7195       if ( nbUniqueNodes == 4 ) {
7196         // ---------------------------------> tetrahedron
7197         if ( curNodes[3] == curNodes[4] &&
7198              curNodes[3] == curNodes[5] ) {
7199           // top nodes stick
7200           toRemove = false;
7201         }
7202         else if ( curNodes[0] == curNodes[1] &&
7203                   curNodes[0] == curNodes[2] ) {
7204           // bottom nodes stick: set a top before
7205           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7206           uniqueNodes[ 0 ] = curNodes [ 5 ];
7207           uniqueNodes[ 1 ] = curNodes [ 4 ];
7208           uniqueNodes[ 2 ] = curNodes [ 3 ];
7209           toRemove = false;
7210         }
7211         else if (( curNodes[0] == curNodes[3] ) +
7212                  ( curNodes[1] == curNodes[4] ) +
7213                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7214           // a lateral face turns into a line
7215           toRemove = false;
7216         }
7217       }
7218       else if ( nbUniqueNodes == 5 ) {
7219         // PENTAHEDRON --------------------> pyramid
7220         if ( curNodes[0] == curNodes[3] )
7221         {
7222           uniqueNodes[ 0 ] = curNodes[ 1 ];
7223           uniqueNodes[ 1 ] = curNodes[ 4 ];
7224           uniqueNodes[ 2 ] = curNodes[ 5 ];
7225           uniqueNodes[ 3 ] = curNodes[ 2 ];
7226           uniqueNodes[ 4 ] = curNodes[ 0 ];
7227           toRemove = false;
7228         }
7229         if ( curNodes[1] == curNodes[4] )
7230         {
7231           uniqueNodes[ 0 ] = curNodes[ 0 ];
7232           uniqueNodes[ 1 ] = curNodes[ 2 ];
7233           uniqueNodes[ 2 ] = curNodes[ 5 ];
7234           uniqueNodes[ 3 ] = curNodes[ 3 ];
7235           uniqueNodes[ 4 ] = curNodes[ 1 ];
7236           toRemove = false;
7237         }
7238         if ( curNodes[2] == curNodes[5] )
7239         {
7240           uniqueNodes[ 0 ] = curNodes[ 0 ];
7241           uniqueNodes[ 1 ] = curNodes[ 3 ];
7242           uniqueNodes[ 2 ] = curNodes[ 4 ];
7243           uniqueNodes[ 3 ] = curNodes[ 1 ];
7244           uniqueNodes[ 4 ] = curNodes[ 2 ];
7245           toRemove = false;
7246         }
7247       }
7248       break;
7249     }
7250     case SMDSEntity_Hexa:
7251     {
7252       //////////////////////////////////// HEXAHEDRON
7253       SMDS_VolumeTool hexa (elem);
7254       hexa.SetExternalNormal();
7255       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7256         //////////////////////// HEX ---> tetrahedron
7257         for ( int iFace = 0; iFace < 6; iFace++ ) {
7258           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7259           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7260               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7261               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7262             // one face turns into a point ...
7263             int  pickInd = ind[ 0 ];
7264             int iOppFace = hexa.GetOppFaceIndex( iFace );
7265             ind = hexa.GetFaceNodesIndices( iOppFace );
7266             int nbStick = 0;
7267             uniqueNodes.clear();
7268             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7269               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7270                 nbStick++;
7271               else
7272                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7273             }
7274             if ( nbStick == 1 ) {
7275               // ... and the opposite one - into a triangle.
7276               // set a top node
7277               uniqueNodes.push_back( curNodes[ pickInd ]);
7278               toRemove = false;
7279             }
7280             break;
7281           }
7282         }
7283       }
7284       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7285         //////////////////////// HEX ---> prism
7286         int nbTria = 0, iTria[3];
7287         const int *ind; // indices of face nodes
7288         // look for triangular faces
7289         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7290           ind = hexa.GetFaceNodesIndices( iFace );
7291           TIDSortedNodeSet faceNodes;
7292           for ( iCur = 0; iCur < 4; iCur++ )
7293             faceNodes.insert( curNodes[ind[iCur]] );
7294           if ( faceNodes.size() == 3 )
7295             iTria[ nbTria++ ] = iFace;
7296         }
7297         // check if triangles are opposite
7298         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7299         {
7300           // set nodes of the bottom triangle
7301           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7302           vector<int> indB;
7303           for ( iCur = 0; iCur < 4; iCur++ )
7304             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7305               indB.push_back( ind[iCur] );
7306           if ( !hexa.IsForward() )
7307             std::swap( indB[0], indB[2] );
7308           for ( iCur = 0; iCur < 3; iCur++ )
7309             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7310           // set nodes of the top triangle
7311           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7312           for ( iCur = 0; iCur < 3; ++iCur )
7313             for ( int j = 0; j < 4; ++j )
7314               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7315               {
7316                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7317                 break;
7318               }
7319           toRemove = false;
7320           break;
7321         }
7322       }
7323       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7324         //////////////////// HEXAHEDRON ---> pyramid
7325         for ( int iFace = 0; iFace < 6; iFace++ ) {
7326           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7327           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7328               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7329               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7330             // one face turns into a point ...
7331             int iOppFace = hexa.GetOppFaceIndex( iFace );
7332             ind = hexa.GetFaceNodesIndices( iOppFace );
7333             uniqueNodes.clear();
7334             for ( iCur = 0; iCur < 4; iCur++ ) {
7335               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7336                 break;
7337               else
7338                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7339             }
7340             if ( uniqueNodes.size() == 4 ) {
7341               // ... and the opposite one is a quadrangle
7342               // set a top node
7343               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7344               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7345               toRemove = false;
7346             }
7347             break;
7348           }
7349         }
7350       }
7351
7352       if ( toRemove && nbUniqueNodes > 4 ) {
7353         ////////////////// HEXAHEDRON ---> polyhedron
7354         hexa.SetExternalNormal();
7355         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7356         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7357         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7358         quantities.reserve( 6 );     quantities.clear();
7359         for ( int iFace = 0; iFace < 6; iFace++ )
7360         {
7361           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7362           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7363                curNodes[ind[1]] == curNodes[ind[3]] )
7364           {
7365             quantities.clear();
7366             break; // opposite nodes stick
7367           }
7368           nodeSet.clear();
7369           for ( iCur = 0; iCur < 4; iCur++ )
7370           {
7371             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7372               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7373           }
7374           if ( nodeSet.size() < 3 )
7375             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7376           else
7377             quantities.push_back( nodeSet.size() );
7378         }
7379         if ( quantities.size() >= 4 )
7380         {
7381           nbResElems = 1;
7382           nbUniqueNodes = poly_nodes.size();
7383           newElemDefs[0].SetPoly(true);
7384         }
7385       }
7386       break;
7387     } // case HEXAHEDRON
7388
7389     default:
7390       toRemove = true;
7391
7392     } // switch ( entity )
7393
7394     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7395     {
7396       // erase from nodeNodeMap nodes whose merge spoils elem
7397       vector< const SMDS_MeshNode* > noMergeNodes;
7398       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7399       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7400         nodeNodeMap.erase( noMergeNodes[i] );
7401     }
7402     
7403   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7404
7405   uniqueNodes.resize( nbUniqueNodes );
7406
7407   if ( !toRemove && nbResElems == 0 )
7408     nbResElems = 1;
7409
7410   newElemDefs.resize( nbResElems );
7411
7412   return !toRemove;
7413 }
7414
7415
7416 // ========================================================
7417 // class   : ComparableElement
7418 // purpose : allow comparing elements basing on their nodes
7419 // ========================================================
7420
7421 class ComparableElement : public boost::container::flat_set< int >
7422 {
7423   typedef boost::container::flat_set< int >  int_set;
7424
7425   const SMDS_MeshElement* myElem;
7426   int                     mySumID;
7427   mutable int             myGroupID;
7428
7429 public:
7430
7431   ComparableElement( const SMDS_MeshElement* theElem ):
7432     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7433   {
7434     this->reserve( theElem->NbNodes() );
7435     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7436     {
7437       int id = nodeIt->next()->GetID();
7438       mySumID += id;
7439       this->insert( id );
7440     }
7441   }
7442
7443   const SMDS_MeshElement* GetElem() const { return myElem; }
7444
7445   int& GroupID() const { return myGroupID; }
7446   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7447
7448   ComparableElement( const ComparableElement& theSource ) // move copy
7449   {
7450     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7451     (int_set&) (*this ) = boost::move( src );
7452     myElem    = src.myElem;
7453     mySumID   = src.mySumID;
7454     myGroupID = src.myGroupID;
7455   }
7456
7457   static int HashCode(const ComparableElement& se, int limit )
7458   {
7459     return ::HashCode( se.mySumID, limit );
7460   }
7461   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7462   {
7463     return ( se1 == se2 );
7464   }
7465
7466 };
7467
7468 //=======================================================================
7469 //function : FindEqualElements
7470 //purpose  : Return list of group of elements built on the same nodes.
7471 //           Search among theElements or in the whole mesh if theElements is empty
7472 //=======================================================================
7473
7474 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7475                                           TListOfListOfElementsID & theGroupsOfElementsID )
7476 {
7477   ClearLastCreated();
7478
7479   SMDS_ElemIteratorPtr elemIt;
7480   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7481   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7482
7483   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7484   typedef std::list<int>                                          TGroupOfElems;
7485   TMapOfElements               mapOfElements;
7486   std::vector< TGroupOfElems > arrayOfGroups;
7487   TGroupOfElems                groupOfElems;
7488
7489   while ( elemIt->more() )
7490   {
7491     const SMDS_MeshElement* curElem = elemIt->next();
7492     if ( curElem->IsNull() )
7493       continue;
7494     ComparableElement      compElem = curElem;
7495     // check uniqueness
7496     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7497     if ( elemInSet.GetElem() != curElem ) // coincident elem
7498     {
7499       int& iG = elemInSet.GroupID();
7500       if ( iG < 0 )
7501       {
7502         iG = arrayOfGroups.size();
7503         arrayOfGroups.push_back( groupOfElems );
7504         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7505       }
7506       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7507     }
7508   }
7509
7510   groupOfElems.clear();
7511   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7512   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7513   {
7514     if ( groupIt->size() > 1 ) {
7515       //groupOfElems.sort(); -- theElements are sorted already
7516       theGroupsOfElementsID.emplace_back( *groupIt );
7517     }
7518   }
7519 }
7520
7521 //=======================================================================
7522 //function : MergeElements
7523 //purpose  : In each given group, substitute all elements by the first one.
7524 //=======================================================================
7525
7526 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7527 {
7528   ClearLastCreated();
7529
7530   typedef list<int> TListOfIDs;
7531   TListOfIDs rmElemIds; // IDs of elems to remove
7532
7533   SMESHDS_Mesh* aMesh = GetMeshDS();
7534
7535   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7536   while ( groupsIt != theGroupsOfElementsID.end() ) {
7537     TListOfIDs& aGroupOfElemID = *groupsIt;
7538     aGroupOfElemID.sort();
7539     int elemIDToKeep = aGroupOfElemID.front();
7540     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7541     aGroupOfElemID.pop_front();
7542     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7543     while ( idIt != aGroupOfElemID.end() ) {
7544       int elemIDToRemove = *idIt;
7545       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7546       // add the kept element in groups of removed one (PAL15188)
7547       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7548       rmElemIds.push_back( elemIDToRemove );
7549       ++idIt;
7550     }
7551     ++groupsIt;
7552   }
7553
7554   Remove( rmElemIds, false );
7555 }
7556
7557 //=======================================================================
7558 //function : MergeEqualElements
7559 //purpose  : Remove all but one of elements built on the same nodes.
7560 //=======================================================================
7561
7562 void SMESH_MeshEditor::MergeEqualElements()
7563 {
7564   TIDSortedElemSet aMeshElements; /* empty input ==
7565                                      to merge equal elements in the whole mesh */
7566   TListOfListOfElementsID aGroupsOfElementsID;
7567   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7568   MergeElements( aGroupsOfElementsID );
7569 }
7570
7571 //=======================================================================
7572 //function : findAdjacentFace
7573 //purpose  :
7574 //=======================================================================
7575
7576 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7577                                                 const SMDS_MeshNode* n2,
7578                                                 const SMDS_MeshElement* elem)
7579 {
7580   TIDSortedElemSet elemSet, avoidSet;
7581   if ( elem )
7582     avoidSet.insert ( elem );
7583   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7584 }
7585
7586 //=======================================================================
7587 //function : findSegment
7588 //purpose  : Return a mesh segment by two nodes one of which can be medium
7589 //=======================================================================
7590
7591 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7592                                            const SMDS_MeshNode* n2)
7593 {
7594   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7595   while ( it->more() )
7596   {
7597     const SMDS_MeshElement* seg = it->next();
7598     if ( seg->GetNodeIndex( n2 ) >= 0 )
7599       return seg;
7600   }
7601   return 0;
7602 }
7603
7604 //=======================================================================
7605 //function : FindFreeBorder
7606 //purpose  :
7607 //=======================================================================
7608
7609 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7610
7611 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7612                                        const SMDS_MeshNode*             theSecondNode,
7613                                        const SMDS_MeshNode*             theLastNode,
7614                                        list< const SMDS_MeshNode* > &   theNodes,
7615                                        list< const SMDS_MeshElement* >& theFaces)
7616 {
7617   if ( !theFirstNode || !theSecondNode )
7618     return false;
7619   // find border face between theFirstNode and theSecondNode
7620   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7621   if ( !curElem )
7622     return false;
7623
7624   theFaces.push_back( curElem );
7625   theNodes.push_back( theFirstNode );
7626   theNodes.push_back( theSecondNode );
7627
7628   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7629   //TIDSortedElemSet foundElems;
7630   bool needTheLast = ( theLastNode != 0 );
7631
7632   vector<const SMDS_MeshNode*> nodes;
7633   
7634   while ( nStart != theLastNode ) {
7635     if ( nStart == theFirstNode )
7636       return !needTheLast;
7637
7638     // find all free border faces sharing nStart
7639
7640     list< const SMDS_MeshElement* > curElemList;
7641     list< const SMDS_MeshNode* >    nStartList;
7642     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7643     while ( invElemIt->more() ) {
7644       const SMDS_MeshElement* e = invElemIt->next();
7645       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7646       {
7647         // get nodes
7648         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7649                       SMDS_MeshElement::iterator() );
7650         nodes.push_back( nodes[ 0 ]);
7651
7652         // check 2 links
7653         int iNode = 0, nbNodes = nodes.size() - 1;
7654         for ( iNode = 0; iNode < nbNodes; iNode++ )
7655           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7656                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7657               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7658           {
7659             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7660             curElemList.push_back( e );
7661           }
7662       }
7663     }
7664     // analyse the found
7665
7666     int nbNewBorders = curElemList.size();
7667     if ( nbNewBorders == 0 ) {
7668       // no free border furthermore
7669       return !needTheLast;
7670     }
7671     else if ( nbNewBorders == 1 ) {
7672       // one more element found
7673       nIgnore = nStart;
7674       nStart = nStartList.front();
7675       curElem = curElemList.front();
7676       theFaces.push_back( curElem );
7677       theNodes.push_back( nStart );
7678     }
7679     else {
7680       // several continuations found
7681       list< const SMDS_MeshElement* >::iterator curElemIt;
7682       list< const SMDS_MeshNode* >::iterator nStartIt;
7683       // check if one of them reached the last node
7684       if ( needTheLast ) {
7685         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7686              curElemIt!= curElemList.end();
7687              curElemIt++, nStartIt++ )
7688           if ( *nStartIt == theLastNode ) {
7689             theFaces.push_back( *curElemIt );
7690             theNodes.push_back( *nStartIt );
7691             return true;
7692           }
7693       }
7694       // find the best free border by the continuations
7695       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7696       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7697       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7698            curElemIt!= curElemList.end();
7699            curElemIt++, nStartIt++ )
7700       {
7701         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7702         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7703         // find one more free border
7704         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7705           cNL->clear();
7706           cFL->clear();
7707         }
7708         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7709           // choice: clear a worse one
7710           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7711           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7712           contNodes[ iWorse ].clear();
7713           contFaces[ iWorse ].clear();
7714         }
7715       }
7716       if ( contNodes[0].empty() && contNodes[1].empty() )
7717         return false;
7718
7719       // push_back the best free border
7720       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7721       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7722       //theNodes.pop_back(); // remove nIgnore
7723       theNodes.pop_back(); // remove nStart
7724       //theFaces.pop_back(); // remove curElem
7725       theNodes.splice( theNodes.end(), *cNL );
7726       theFaces.splice( theFaces.end(), *cFL );
7727       return true;
7728
7729     } // several continuations found
7730   } // while ( nStart != theLastNode )
7731
7732   return true;
7733 }
7734
7735 //=======================================================================
7736 //function : CheckFreeBorderNodes
7737 //purpose  : Return true if the tree nodes are on a free border
7738 //=======================================================================
7739
7740 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7741                                             const SMDS_MeshNode* theNode2,
7742                                             const SMDS_MeshNode* theNode3)
7743 {
7744   list< const SMDS_MeshNode* > nodes;
7745   list< const SMDS_MeshElement* > faces;
7746   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7747 }
7748
7749 //=======================================================================
7750 //function : SewFreeBorder
7751 //purpose  :
7752 //warning  : for border-to-side sewing theSideSecondNode is considered as
7753 //           the last side node and theSideThirdNode is not used
7754 //=======================================================================
7755
7756 SMESH_MeshEditor::Sew_Error
7757 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7758                                  const SMDS_MeshNode* theBordSecondNode,
7759                                  const SMDS_MeshNode* theBordLastNode,
7760                                  const SMDS_MeshNode* theSideFirstNode,
7761                                  const SMDS_MeshNode* theSideSecondNode,
7762                                  const SMDS_MeshNode* theSideThirdNode,
7763                                  const bool           theSideIsFreeBorder,
7764                                  const bool           toCreatePolygons,
7765                                  const bool           toCreatePolyedrs)
7766 {
7767   ClearLastCreated();
7768
7769   Sew_Error aResult = SEW_OK;
7770
7771   // ====================================
7772   //    find side nodes and elements
7773   // ====================================
7774
7775   list< const SMDS_MeshNode* >    nSide[ 2 ];
7776   list< const SMDS_MeshElement* > eSide[ 2 ];
7777   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7778   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7779
7780   // Free border 1
7781   // --------------
7782   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7783                       nSide[0], eSide[0])) {
7784     MESSAGE(" Free Border 1 not found " );
7785     aResult = SEW_BORDER1_NOT_FOUND;
7786   }
7787   if (theSideIsFreeBorder) {
7788     // Free border 2
7789     // --------------
7790     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7791                         nSide[1], eSide[1])) {
7792       MESSAGE(" Free Border 2 not found " );
7793       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7794     }
7795   }
7796   if ( aResult != SEW_OK )
7797     return aResult;
7798
7799   if (!theSideIsFreeBorder) {
7800     // Side 2
7801     // --------------
7802
7803     // -------------------------------------------------------------------------
7804     // Algo:
7805     // 1. If nodes to merge are not coincident, move nodes of the free border
7806     //    from the coord sys defined by the direction from the first to last
7807     //    nodes of the border to the correspondent sys of the side 2
7808     // 2. On the side 2, find the links most co-directed with the correspondent
7809     //    links of the free border
7810     // -------------------------------------------------------------------------
7811
7812     // 1. Since sewing may break if there are volumes to split on the side 2,
7813     //    we won't move nodes but just compute new coordinates for them
7814     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7815     TNodeXYZMap nBordXYZ;
7816     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7817     list< const SMDS_MeshNode* >::iterator nBordIt;
7818
7819     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7820     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7821     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7822     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7823     double tol2 = 1.e-8;
7824     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7825     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7826       // Need node movement.
7827
7828       // find X and Z axes to create trsf
7829       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7830       gp_Vec X = Zs ^ Zb;
7831       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7832         // Zb || Zs
7833         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7834
7835       // coord systems
7836       gp_Ax3 toBordAx( Pb1, Zb, X );
7837       gp_Ax3 fromSideAx( Ps1, Zs, X );
7838       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7839       // set trsf
7840       gp_Trsf toBordSys, fromSide2Sys;
7841       toBordSys.SetTransformation( toBordAx );
7842       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7843       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7844
7845       // move
7846       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7847         const SMDS_MeshNode* n = *nBordIt;
7848         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7849         toBordSys.Transforms( xyz );
7850         fromSide2Sys.Transforms( xyz );
7851         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7852       }
7853     }
7854     else {
7855       // just insert nodes XYZ in the nBordXYZ map
7856       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7857         const SMDS_MeshNode* n = *nBordIt;
7858         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7859       }
7860     }
7861
7862     // 2. On the side 2, find the links most co-directed with the correspondent
7863     //    links of the free border
7864
7865     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7866     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7867     sideNodes.push_back( theSideFirstNode );
7868
7869     bool hasVolumes = false;
7870     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7871     set<long> foundSideLinkIDs, checkedLinkIDs;
7872     SMDS_VolumeTool volume;
7873     //const SMDS_MeshNode* faceNodes[ 4 ];
7874
7875     const SMDS_MeshNode*    sideNode;
7876     const SMDS_MeshElement* sideElem  = 0;
7877     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7878     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7879     nBordIt = bordNodes.begin();
7880     nBordIt++;
7881     // border node position and border link direction to compare with
7882     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7883     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7884     // choose next side node by link direction or by closeness to
7885     // the current border node:
7886     bool searchByDir = ( *nBordIt != theBordLastNode );
7887     do {
7888       // find the next node on the Side 2
7889       sideNode = 0;
7890       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7891       long linkID;
7892       checkedLinkIDs.clear();
7893       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7894
7895       // loop on inverse elements of current node (prevSideNode) on the Side 2
7896       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7897       while ( invElemIt->more() )
7898       {
7899         const SMDS_MeshElement* elem = invElemIt->next();
7900         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7901         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7902         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7903         bool isVolume = volume.Set( elem );
7904         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7905         if ( isVolume ) // --volume
7906           hasVolumes = true;
7907         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7908           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7909           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7910           while ( nIt->more() ) {
7911             nodes[ iNode ] = cast2Node( nIt->next() );
7912             if ( nodes[ iNode++ ] == prevSideNode )
7913               iPrevNode = iNode - 1;
7914           }
7915           // there are 2 links to check
7916           nbNodes = 2;
7917         }
7918         else // --edge
7919           continue;
7920         // loop on links, to be precise, on the second node of links
7921         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7922           const SMDS_MeshNode* n = nodes[ iNode ];
7923           if ( isVolume ) {
7924             if ( !volume.IsLinked( n, prevSideNode ))
7925               continue;
7926           }
7927           else {
7928             if ( iNode ) // a node before prevSideNode
7929               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7930             else         // a node after prevSideNode
7931               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7932           }
7933           // check if this link was already used
7934           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7935           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7936           if (!isJustChecked &&
7937               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7938           {
7939             // test a link geometrically
7940             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7941             bool linkIsBetter = false;
7942             double dot = 0.0, dist = 0.0;
7943             if ( searchByDir ) { // choose most co-directed link
7944               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7945               linkIsBetter = ( dot > maxDot );
7946             }
7947             else { // choose link with the node closest to bordPos
7948               dist = ( nextXYZ - bordPos ).SquareModulus();
7949               linkIsBetter = ( dist < minDist );
7950             }
7951             if ( linkIsBetter ) {
7952               maxDot = dot;
7953               minDist = dist;
7954               linkID = iLink;
7955               sideNode = n;
7956               sideElem = elem;
7957             }
7958           }
7959         }
7960       } // loop on inverse elements of prevSideNode
7961
7962       if ( !sideNode ) {
7963         MESSAGE(" Can't find path by links of the Side 2 ");
7964         return SEW_BAD_SIDE_NODES;
7965       }
7966       sideNodes.push_back( sideNode );
7967       sideElems.push_back( sideElem );
7968       foundSideLinkIDs.insert ( linkID );
7969       prevSideNode = sideNode;
7970
7971       if ( *nBordIt == theBordLastNode )
7972         searchByDir = false;
7973       else {
7974         // find the next border link to compare with
7975         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7976         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7977         // move to next border node if sideNode is before forward border node (bordPos)
7978         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7979           prevBordNode = *nBordIt;
7980           nBordIt++;
7981           bordPos = nBordXYZ[ *nBordIt ];
7982           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7983           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7984         }
7985       }
7986     }
7987     while ( sideNode != theSideSecondNode );
7988
7989     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7990       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7991       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7992     }
7993   } // end nodes search on the side 2
7994
7995   // ============================
7996   // sew the border to the side 2
7997   // ============================
7998
7999   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8000   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8001
8002   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8003   if ( toMergeConformal && toCreatePolygons )
8004   {
8005     // do not merge quadrangles if polygons are OK (IPAL0052824)
8006     eIt[0] = eSide[0].begin();
8007     eIt[1] = eSide[1].begin();
8008     bool allQuads[2] = { true, true };
8009     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8010       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8011         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8012     }
8013     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8014   }
8015
8016   TListOfListOfNodes nodeGroupsToMerge;
8017   if (( toMergeConformal ) ||
8018       ( theSideIsFreeBorder && !theSideThirdNode )) {
8019
8020     // all nodes are to be merged
8021
8022     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8023          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8024          nIt[0]++, nIt[1]++ )
8025     {
8026       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8027       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8028       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8029     }
8030   }
8031   else {
8032
8033     // insert new nodes into the border and the side to get equal nb of segments
8034
8035     // get normalized parameters of nodes on the borders
8036     vector< double > param[ 2 ];
8037     param[0].resize( maxNbNodes );
8038     param[1].resize( maxNbNodes );
8039     int iNode, iBord;
8040     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8041       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8042       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8043       const SMDS_MeshNode* nPrev = *nIt;
8044       double bordLength = 0;
8045       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8046         const SMDS_MeshNode* nCur = *nIt;
8047         gp_XYZ segment (nCur->X() - nPrev->X(),
8048                         nCur->Y() - nPrev->Y(),
8049                         nCur->Z() - nPrev->Z());
8050         double segmentLen = segment.Modulus();
8051         bordLength += segmentLen;
8052         param[ iBord ][ iNode ] = bordLength;
8053         nPrev = nCur;
8054       }
8055       // normalize within [0,1]
8056       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8057         param[ iBord ][ iNode ] /= bordLength;
8058       }
8059     }
8060
8061     // loop on border segments
8062     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8063     int i[ 2 ] = { 0, 0 };
8064     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8065     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8066
8067     // element can be split while iterating on border if it has two edges in the border
8068     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8069     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8070
8071     TElemOfNodeListMap insertMap;
8072     TElemOfNodeListMap::iterator insertMapIt;
8073     // insertMap is
8074     // key:   elem to insert nodes into
8075     // value: 2 nodes to insert between + nodes to be inserted
8076     do {
8077       bool next[ 2 ] = { false, false };
8078
8079       // find min adjacent segment length after sewing
8080       double nextParam = 10., prevParam = 0;
8081       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8082         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8083           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8084         if ( i[ iBord ] > 0 )
8085           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8086       }
8087       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8088       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8089       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8090
8091       // choose to insert or to merge nodes
8092       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8093       if ( Abs( du ) <= minSegLen * 0.2 ) {
8094         // merge
8095         // ------
8096         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8097         const SMDS_MeshNode* n0 = *nIt[0];
8098         const SMDS_MeshNode* n1 = *nIt[1];
8099         nodeGroupsToMerge.back().push_back( n1 );
8100         nodeGroupsToMerge.back().push_back( n0 );
8101         // position of node of the border changes due to merge
8102         param[ 0 ][ i[0] ] += du;
8103         // move n1 for the sake of elem shape evaluation during insertion.
8104         // n1 will be removed by MergeNodes() anyway
8105         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8106         next[0] = next[1] = true;
8107       }
8108       else {
8109         // insert
8110         // ------
8111         int intoBord = ( du < 0 ) ? 0 : 1;
8112         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8113         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8114         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8115         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8116         if ( intoBord == 1 ) {
8117           // move node of the border to be on a link of elem of the side
8118           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8119           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8120           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8121           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8122         }
8123         elemReplaceMapIt = elemReplaceMap.find( elem );
8124         if ( elemReplaceMapIt != elemReplaceMap.end() )
8125           elem = elemReplaceMapIt->second;
8126
8127         insertMapIt = insertMap.find( elem );
8128         bool  notFound = ( insertMapIt == insertMap.end() );
8129         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8130         if ( otherLink ) {
8131           // insert into another link of the same element:
8132           // 1. perform insertion into the other link of the elem
8133           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8134           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8135           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8136           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8137           // 2. perform insertion into the link of adjacent faces
8138           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8139             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8140           }
8141           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8142             InsertNodesIntoLink( seg, n12, n22, nodeList );
8143           }
8144           if (toCreatePolyedrs) {
8145             // perform insertion into the links of adjacent volumes
8146             UpdateVolumes(n12, n22, nodeList);
8147           }
8148           // 3. find an element appeared on n1 and n2 after the insertion
8149           insertMap.erase( insertMapIt );
8150           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8151           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8152           elem = elem2;
8153         }
8154         if ( notFound || otherLink ) {
8155           // add element and nodes of the side into the insertMap
8156           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8157           (*insertMapIt).second.push_back( n1 );
8158           (*insertMapIt).second.push_back( n2 );
8159         }
8160         // add node to be inserted into elem
8161         (*insertMapIt).second.push_back( nIns );
8162         next[ 1 - intoBord ] = true;
8163       }
8164
8165       // go to the next segment
8166       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8167         if ( next[ iBord ] ) {
8168           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8169             eIt[ iBord ]++;
8170           nPrev[ iBord ] = *nIt[ iBord ];
8171           nIt[ iBord ]++; i[ iBord ]++;
8172         }
8173       }
8174     }
8175     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8176
8177     // perform insertion of nodes into elements
8178
8179     for (insertMapIt = insertMap.begin();
8180          insertMapIt != insertMap.end();
8181          insertMapIt++ )
8182     {
8183       const SMDS_MeshElement* elem = (*insertMapIt).first;
8184       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8185       if ( nodeList.size() < 3 ) continue;
8186       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8187       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8188
8189       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8190
8191       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8192         InsertNodesIntoLink( seg, n1, n2, nodeList );
8193       }
8194
8195       if ( !theSideIsFreeBorder ) {
8196         // look for and insert nodes into the faces adjacent to elem
8197         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8198           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8199         }
8200       }
8201       if (toCreatePolyedrs) {
8202         // perform insertion into the links of adjacent volumes
8203         UpdateVolumes(n1, n2, nodeList);
8204       }
8205     }
8206   } // end: insert new nodes
8207
8208   MergeNodes ( nodeGroupsToMerge );
8209
8210
8211   // Remove coincident segments
8212
8213   // get new segments
8214   TIDSortedElemSet segments;
8215   SMESH_SequenceOfElemPtr newFaces;
8216   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8217   {
8218     if ( !myLastCreatedElems[i] ) continue;
8219     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8220       segments.insert( segments.end(), myLastCreatedElems[i] );
8221     else
8222       newFaces.push_back( myLastCreatedElems[i] );
8223   }
8224   // get segments adjacent to merged nodes
8225   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8226   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8227   {
8228     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8229     if ( nodes.front()->IsNull() ) continue;
8230     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8231     while ( segIt->more() )
8232       segments.insert( segIt->next() );
8233   }
8234
8235   // find coincident
8236   TListOfListOfElementsID equalGroups;
8237   if ( !segments.empty() )
8238     FindEqualElements( segments, equalGroups );
8239   if ( !equalGroups.empty() )
8240   {
8241     // remove from segments those that will be removed
8242     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8243     for ( ; itGroups != equalGroups.end(); ++itGroups )
8244     {
8245       list< int >& group = *itGroups;
8246       list< int >::iterator id = group.begin();
8247       for ( ++id; id != group.end(); ++id )
8248         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8249           segments.erase( seg );
8250     }
8251     // remove equal segments
8252     MergeElements( equalGroups );
8253
8254     // restore myLastCreatedElems
8255     myLastCreatedElems = newFaces;
8256     TIDSortedElemSet::iterator seg = segments.begin();
8257     for ( ; seg != segments.end(); ++seg )
8258       myLastCreatedElems.push_back( *seg );
8259   }
8260
8261   return aResult;
8262 }
8263
8264 //=======================================================================
8265 //function : InsertNodesIntoLink
8266 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8267 //           and theBetweenNode2 and split theElement
8268 //=======================================================================
8269
8270 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8271                                            const SMDS_MeshNode*        theBetweenNode1,
8272                                            const SMDS_MeshNode*        theBetweenNode2,
8273                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8274                                            const bool                  toCreatePoly)
8275 {
8276   if ( !theElement ) return;
8277
8278   SMESHDS_Mesh *aMesh = GetMeshDS();
8279   vector<const SMDS_MeshElement*> newElems;
8280
8281   if ( theElement->GetType() == SMDSAbs_Edge )
8282   {
8283     theNodesToInsert.push_front( theBetweenNode1 );
8284     theNodesToInsert.push_back ( theBetweenNode2 );
8285     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8286     const SMDS_MeshNode* n1 = *n;
8287     for ( ++n; n != theNodesToInsert.end(); ++n )
8288     {
8289       const SMDS_MeshNode* n2 = *n;
8290       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8291         AddToSameGroups( seg, theElement, aMesh );
8292       else
8293         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8294       n1 = n2;
8295     }
8296     theNodesToInsert.pop_front();
8297     theNodesToInsert.pop_back();
8298
8299     if ( theElement->IsQuadratic() ) // add a not split part
8300     {
8301       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8302                                           theElement->end_nodes() );
8303       int iOther = 0, nbN = nodes.size();
8304       for ( ; iOther < nbN; ++iOther )
8305         if ( nodes[iOther] != theBetweenNode1 &&
8306              nodes[iOther] != theBetweenNode2 )
8307           break;
8308       if      ( iOther == 0 )
8309       {
8310         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8311           AddToSameGroups( seg, theElement, aMesh );
8312         else
8313           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8314       }
8315       else if ( iOther == 2 )
8316       {
8317         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8318           AddToSameGroups( seg, theElement, aMesh );
8319         else
8320           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8321       }
8322     }
8323     // treat new elements
8324     for ( size_t i = 0; i < newElems.size(); ++i )
8325       if ( newElems[i] )
8326       {
8327         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8328         myLastCreatedElems.push_back( newElems[i] );
8329       }
8330     ReplaceElemInGroups( theElement, newElems, aMesh );
8331     aMesh->RemoveElement( theElement );
8332     return;
8333
8334   } // if ( theElement->GetType() == SMDSAbs_Edge )
8335
8336   const SMDS_MeshElement* theFace = theElement;
8337   if ( theFace->GetType() != SMDSAbs_Face ) return;
8338
8339   // find indices of 2 link nodes and of the rest nodes
8340   int iNode = 0, il1, il2, i3, i4;
8341   il1 = il2 = i3 = i4 = -1;
8342   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8343
8344   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8345   while ( nodeIt->more() ) {
8346     const SMDS_MeshNode* n = nodeIt->next();
8347     if ( n == theBetweenNode1 )
8348       il1 = iNode;
8349     else if ( n == theBetweenNode2 )
8350       il2 = iNode;
8351     else if ( i3 < 0 )
8352       i3 = iNode;
8353     else
8354       i4 = iNode;
8355     nodes[ iNode++ ] = n;
8356   }
8357   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8358     return ;
8359
8360   // arrange link nodes to go one after another regarding the face orientation
8361   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8362   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8363   if ( reverse ) {
8364     iNode = il1;
8365     il1 = il2;
8366     il2 = iNode;
8367     aNodesToInsert.reverse();
8368   }
8369   // check that not link nodes of a quadrangles are in good order
8370   int nbFaceNodes = theFace->NbNodes();
8371   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8372     iNode = i3;
8373     i3 = i4;
8374     i4 = iNode;
8375   }
8376
8377   if (toCreatePoly || theFace->IsPoly()) {
8378
8379     iNode = 0;
8380     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8381
8382     // add nodes of face up to first node of link
8383     bool isFLN = false;
8384     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8385     while ( nodeIt->more() && !isFLN ) {
8386       const SMDS_MeshNode* n = nodeIt->next();
8387       poly_nodes[iNode++] = n;
8388       isFLN = ( n == nodes[il1] );
8389     }
8390     // add nodes to insert
8391     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8392     for (; nIt != aNodesToInsert.end(); nIt++) {
8393       poly_nodes[iNode++] = *nIt;
8394     }
8395     // add nodes of face starting from last node of link
8396     while ( nodeIt->more() ) {
8397       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8398       poly_nodes[iNode++] = n;
8399     }
8400
8401     // make a new face
8402     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8403   }
8404
8405   else if ( !theFace->IsQuadratic() )
8406   {
8407     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8408     int nbLinkNodes = 2 + aNodesToInsert.size();
8409     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8410     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8411     linkNodes[ 0 ] = nodes[ il1 ];
8412     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8413     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8414     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8415       linkNodes[ iNode++ ] = *nIt;
8416     }
8417     // decide how to split a quadrangle: compare possible variants
8418     // and choose which of splits to be a quadrangle
8419     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8420     if ( nbFaceNodes == 3 ) {
8421       iBestQuad = nbSplits;
8422       i4 = i3;
8423     }
8424     else if ( nbFaceNodes == 4 ) {
8425       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8426       double aBestRate = DBL_MAX;
8427       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8428         i1 = 0; i2 = 1;
8429         double aBadRate = 0;
8430         // evaluate elements quality
8431         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8432           if ( iSplit == iQuad ) {
8433             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8434                                    linkNodes[ i2++ ],
8435                                    nodes[ i3 ],
8436                                    nodes[ i4 ]);
8437             aBadRate += getBadRate( &quad, aCrit );
8438           }
8439           else {
8440             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8441                                    linkNodes[ i2++ ],
8442                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8443             aBadRate += getBadRate( &tria, aCrit );
8444           }
8445         }
8446         // choice
8447         if ( aBadRate < aBestRate ) {
8448           iBestQuad = iQuad;
8449           aBestRate = aBadRate;
8450         }
8451       }
8452     }
8453
8454     // create new elements
8455     i1 = 0; i2 = 1;
8456     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8457     {
8458       if ( iSplit == iBestQuad )
8459         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8460                                             linkNodes[ i2++ ],
8461                                             nodes[ i3 ],
8462                                             nodes[ i4 ]));
8463       else
8464         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8465                                             linkNodes[ i2++ ],
8466                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8467     }
8468
8469     const SMDS_MeshNode* newNodes[ 4 ];
8470     newNodes[ 0 ] = linkNodes[ i1 ];
8471     newNodes[ 1 ] = linkNodes[ i2 ];
8472     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8473     newNodes[ 3 ] = nodes[ i4 ];
8474     if (iSplit == iBestQuad)
8475       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8476     else
8477       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8478
8479   } // end if(!theFace->IsQuadratic())
8480
8481   else { // theFace is quadratic
8482     // we have to split theFace on simple triangles and one simple quadrangle
8483     int tmp = il1/2;
8484     int nbshift = tmp*2;
8485     // shift nodes in nodes[] by nbshift
8486     int i,j;
8487     for(i=0; i<nbshift; i++) {
8488       const SMDS_MeshNode* n = nodes[0];
8489       for(j=0; j<nbFaceNodes-1; j++) {
8490         nodes[j] = nodes[j+1];
8491       }
8492       nodes[nbFaceNodes-1] = n;
8493     }
8494     il1 = il1 - nbshift;
8495     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8496     //   n0      n1     n2    n0      n1     n2
8497     //     +-----+-----+        +-----+-----+
8498     //      \         /         |           |
8499     //       \       /          |           |
8500     //      n5+     +n3       n7+           +n3
8501     //         \   /            |           |
8502     //          \ /             |           |
8503     //           +              +-----+-----+
8504     //           n4           n6      n5     n4
8505
8506     // create new elements
8507     int n1,n2,n3;
8508     if ( nbFaceNodes == 6 ) { // quadratic triangle
8509       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8510       if ( theFace->IsMediumNode(nodes[il1]) ) {
8511         // create quadrangle
8512         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8513         n1 = 1;
8514         n2 = 2;
8515         n3 = 3;
8516       }
8517       else {
8518         // create quadrangle
8519         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8520         n1 = 0;
8521         n2 = 1;
8522         n3 = 5;
8523       }
8524     }
8525     else { // nbFaceNodes==8 - quadratic quadrangle
8526       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8527       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8528       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8529       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8530         // create quadrangle
8531         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8532         n1 = 1;
8533         n2 = 2;
8534         n3 = 3;
8535       }
8536       else {
8537         // create quadrangle
8538         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8539         n1 = 0;
8540         n2 = 1;
8541         n3 = 7;
8542       }
8543     }
8544     // create needed triangles using n1,n2,n3 and inserted nodes
8545     int nbn = 2 + aNodesToInsert.size();
8546     vector<const SMDS_MeshNode*> aNodes(nbn);
8547     aNodes[0    ] = nodes[n1];
8548     aNodes[nbn-1] = nodes[n2];
8549     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8550     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8551       aNodes[iNode++] = *nIt;
8552     }
8553     for ( i = 1; i < nbn; i++ )
8554       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8555   }
8556
8557   // remove the old face
8558   for ( size_t i = 0; i < newElems.size(); ++i )
8559     if ( newElems[i] )
8560     {
8561       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8562       myLastCreatedElems.push_back( newElems[i] );
8563     }
8564   ReplaceElemInGroups( theFace, newElems, aMesh );
8565   aMesh->RemoveElement(theFace);
8566
8567 } // InsertNodesIntoLink()
8568
8569 //=======================================================================
8570 //function : UpdateVolumes
8571 //purpose  :
8572 //=======================================================================
8573
8574 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8575                                       const SMDS_MeshNode*        theBetweenNode2,
8576                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8577 {
8578   ClearLastCreated();
8579
8580   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8581   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8582     const SMDS_MeshElement* elem = invElemIt->next();
8583
8584     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8585     SMDS_VolumeTool aVolume (elem);
8586     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8587       continue;
8588
8589     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8590     int iface, nbFaces = aVolume.NbFaces();
8591     vector<const SMDS_MeshNode *> poly_nodes;
8592     vector<int> quantities (nbFaces);
8593
8594     for (iface = 0; iface < nbFaces; iface++) {
8595       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8596       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8597       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8598
8599       for (int inode = 0; inode < nbFaceNodes; inode++) {
8600         poly_nodes.push_back(faceNodes[inode]);
8601
8602         if (nbInserted == 0) {
8603           if (faceNodes[inode] == theBetweenNode1) {
8604             if (faceNodes[inode + 1] == theBetweenNode2) {
8605               nbInserted = theNodesToInsert.size();
8606
8607               // add nodes to insert
8608               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8609               for (; nIt != theNodesToInsert.end(); nIt++) {
8610                 poly_nodes.push_back(*nIt);
8611               }
8612             }
8613           }
8614           else if (faceNodes[inode] == theBetweenNode2) {
8615             if (faceNodes[inode + 1] == theBetweenNode1) {
8616               nbInserted = theNodesToInsert.size();
8617
8618               // add nodes to insert in reversed order
8619               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8620               nIt--;
8621               for (; nIt != theNodesToInsert.begin(); nIt--) {
8622                 poly_nodes.push_back(*nIt);
8623               }
8624               poly_nodes.push_back(*nIt);
8625             }
8626           }
8627           else {
8628           }
8629         }
8630       }
8631       quantities[iface] = nbFaceNodes + nbInserted;
8632     }
8633
8634     // Replace the volume
8635     SMESHDS_Mesh *aMesh = GetMeshDS();
8636
8637     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8638     {
8639       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8640       myLastCreatedElems.push_back( newElem );
8641       ReplaceElemInGroups( elem, newElem, aMesh );
8642     }
8643     aMesh->RemoveElement( elem );
8644   }
8645 }
8646
8647 namespace
8648 {
8649   //================================================================================
8650   /*!
8651    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8652    */
8653   //================================================================================
8654
8655   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8656                            vector<const SMDS_MeshNode *> & nodes,
8657                            vector<int> &                   nbNodeInFaces )
8658   {
8659     nodes.clear();
8660     nbNodeInFaces.clear();
8661     SMDS_VolumeTool vTool ( elem );
8662     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8663     {
8664       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8665       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8666       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8667     }
8668   }
8669 }
8670
8671 //=======================================================================
8672 /*!
8673  * \brief Convert elements contained in a sub-mesh to quadratic
8674  * \return int - nb of checked elements
8675  */
8676 //=======================================================================
8677
8678 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8679                                              SMESH_MesherHelper& theHelper,
8680                                              const bool          theForce3d)
8681 {
8682   //MESSAGE("convertElemToQuadratic");
8683   int nbElem = 0;
8684   if( !theSm ) return nbElem;
8685
8686   vector<int> nbNodeInFaces;
8687   vector<const SMDS_MeshNode *> nodes;
8688   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8689   while(ElemItr->more())
8690   {
8691     nbElem++;
8692     const SMDS_MeshElement* elem = ElemItr->next();
8693     if( !elem ) continue;
8694
8695     // analyse a necessity of conversion
8696     const SMDSAbs_ElementType aType = elem->GetType();
8697     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8698       continue;
8699     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8700     bool hasCentralNodes = false;
8701     if ( elem->IsQuadratic() )
8702     {
8703       bool alreadyOK;
8704       switch ( aGeomType ) {
8705       case SMDSEntity_Quad_Triangle:
8706       case SMDSEntity_Quad_Quadrangle:
8707       case SMDSEntity_Quad_Hexa:
8708       case SMDSEntity_Quad_Penta:
8709         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8710
8711       case SMDSEntity_BiQuad_Triangle:
8712       case SMDSEntity_BiQuad_Quadrangle:
8713       case SMDSEntity_TriQuad_Hexa:
8714       case SMDSEntity_BiQuad_Penta:
8715         alreadyOK = theHelper.GetIsBiQuadratic();
8716         hasCentralNodes = true;
8717         break;
8718       default:
8719         alreadyOK = true;
8720       }
8721       // take into account already present medium nodes
8722       switch ( aType ) {
8723       case SMDSAbs_Volume:
8724         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8725       case SMDSAbs_Face:
8726         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8727       case SMDSAbs_Edge:
8728         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8729       default:;
8730       }
8731       if ( alreadyOK )
8732         continue;
8733     }
8734     // get elem data needed to re-create it
8735     //
8736     const int id      = elem->GetID();
8737     const int nbNodes = elem->NbCornerNodes();
8738     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8739     if ( aGeomType == SMDSEntity_Polyhedra )
8740       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8741     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8742       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8743
8744     // remove a linear element
8745     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8746
8747     // remove central nodes of biquadratic elements (biquad->quad conversion)
8748     if ( hasCentralNodes )
8749       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8750         if ( nodes[i]->NbInverseElements() == 0 )
8751           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8752
8753     const SMDS_MeshElement* NewElem = 0;
8754
8755     switch( aType )
8756     {
8757     case SMDSAbs_Edge :
8758     {
8759       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8760       break;
8761     }
8762     case SMDSAbs_Face :
8763     {
8764       switch(nbNodes)
8765       {
8766       case 3:
8767         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8768         break;
8769       case 4:
8770         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8771         break;
8772       default:
8773         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8774       }
8775       break;
8776     }
8777     case SMDSAbs_Volume :
8778     {
8779       switch( aGeomType )
8780       {
8781       case SMDSEntity_Tetra:
8782         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8783         break;
8784       case SMDSEntity_Pyramid:
8785         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8786         break;
8787       case SMDSEntity_Penta:
8788       case SMDSEntity_Quad_Penta:
8789       case SMDSEntity_BiQuad_Penta:
8790         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8791         break;
8792       case SMDSEntity_Hexa:
8793       case SMDSEntity_Quad_Hexa:
8794       case SMDSEntity_TriQuad_Hexa:
8795         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8796                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8797         break;
8798       case SMDSEntity_Hexagonal_Prism:
8799       default:
8800         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8801       }
8802       break;
8803     }
8804     default :
8805       continue;
8806     }
8807     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8808     if( NewElem && NewElem->getshapeId() < 1 )
8809       theSm->AddElement( NewElem );
8810   }
8811   return nbElem;
8812 }
8813 //=======================================================================
8814 //function : ConvertToQuadratic
8815 //purpose  :
8816 //=======================================================================
8817
8818 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8819 {
8820   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8821   SMESHDS_Mesh* meshDS = GetMeshDS();
8822
8823   SMESH_MesherHelper aHelper(*myMesh);
8824
8825   aHelper.SetIsQuadratic( true );
8826   aHelper.SetIsBiQuadratic( theToBiQuad );
8827   aHelper.SetElementsOnShape(true);
8828   aHelper.ToFixNodeParameters( true );
8829
8830   // convert elements assigned to sub-meshes
8831   int nbCheckedElems = 0;
8832   if ( myMesh->HasShapeToMesh() )
8833   {
8834     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8835     {
8836       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8837       while ( smIt->more() ) {
8838         SMESH_subMesh* sm = smIt->next();
8839         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8840           aHelper.SetSubShape( sm->GetSubShape() );
8841           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8842         }
8843       }
8844     }
8845   }
8846
8847   // convert elements NOT assigned to sub-meshes
8848   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8849   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8850   {
8851     aHelper.SetElementsOnShape(false);
8852     SMESHDS_SubMesh *smDS = 0;
8853
8854     // convert edges
8855     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8856     while( aEdgeItr->more() )
8857     {
8858       const SMDS_MeshEdge* edge = aEdgeItr->next();
8859       if ( !edge->IsQuadratic() )
8860       {
8861         int                  id = edge->GetID();
8862         const SMDS_MeshNode* n1 = edge->GetNode(0);
8863         const SMDS_MeshNode* n2 = edge->GetNode(1);
8864
8865         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8866
8867         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8868         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8869       }
8870       else
8871       {
8872         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8873       }
8874     }
8875
8876     // convert faces
8877     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8878     while( aFaceItr->more() )
8879     {
8880       const SMDS_MeshFace* face = aFaceItr->next();
8881       if ( !face ) continue;
8882       
8883       const SMDSAbs_EntityType type = face->GetEntityType();
8884       bool alreadyOK;
8885       switch( type )
8886       {
8887       case SMDSEntity_Quad_Triangle:
8888       case SMDSEntity_Quad_Quadrangle:
8889         alreadyOK = !theToBiQuad;
8890         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8891         break;
8892       case SMDSEntity_BiQuad_Triangle:
8893       case SMDSEntity_BiQuad_Quadrangle:
8894         alreadyOK = theToBiQuad;
8895         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8896         break;
8897       default: alreadyOK = false;
8898       }
8899       if ( alreadyOK )
8900         continue;
8901
8902       const int id = face->GetID();
8903       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8904
8905       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8906
8907       SMDS_MeshFace * NewFace = 0;
8908       switch( type )
8909       {
8910       case SMDSEntity_Triangle:
8911       case SMDSEntity_Quad_Triangle:
8912       case SMDSEntity_BiQuad_Triangle:
8913         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8914         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8915           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8916         break;
8917
8918       case SMDSEntity_Quadrangle:
8919       case SMDSEntity_Quad_Quadrangle:
8920       case SMDSEntity_BiQuad_Quadrangle:
8921         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8922         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8923           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8924         break;
8925
8926       default:;
8927         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8928       }
8929       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8930     }
8931
8932     // convert volumes
8933     vector<int> nbNodeInFaces;
8934     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8935     while(aVolumeItr->more())
8936     {
8937       const SMDS_MeshVolume* volume = aVolumeItr->next();
8938       if ( !volume ) continue;
8939
8940       const SMDSAbs_EntityType type = volume->GetEntityType();
8941       if ( volume->IsQuadratic() )
8942       {
8943         bool alreadyOK;
8944         switch ( type )
8945         {
8946         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8947         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8948         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8949         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8950         default:                      alreadyOK = true;
8951         }
8952         if ( alreadyOK )
8953         {
8954           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8955           continue;
8956         }
8957       }
8958       const int id = volume->GetID();
8959       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8960       if ( type == SMDSEntity_Polyhedra )
8961         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8962       else if ( type == SMDSEntity_Hexagonal_Prism )
8963         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8964
8965       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8966
8967       SMDS_MeshVolume * NewVolume = 0;
8968       switch ( type )
8969       {
8970       case SMDSEntity_Tetra:
8971         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8972         break;
8973       case SMDSEntity_Hexa:
8974       case SMDSEntity_Quad_Hexa:
8975       case SMDSEntity_TriQuad_Hexa:
8976         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8977                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8978         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8979           if ( nodes[i]->NbInverseElements() == 0 )
8980             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8981         break;
8982       case SMDSEntity_Pyramid:
8983         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8984                                       nodes[3], nodes[4], id, theForce3d);
8985         break;
8986       case SMDSEntity_Penta:
8987       case SMDSEntity_Quad_Penta:
8988       case SMDSEntity_BiQuad_Penta:
8989         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8990                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8991         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8992           if ( nodes[i]->NbInverseElements() == 0 )
8993             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8994         break;
8995       case SMDSEntity_Hexagonal_Prism:
8996       default:
8997         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8998       }
8999       ReplaceElemInGroups(volume, NewVolume, meshDS);
9000     }
9001   }
9002
9003   if ( !theForce3d )
9004   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9005     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9006     // aHelper.FixQuadraticElements(myError);
9007     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9008   }
9009 }
9010
9011 //================================================================================
9012 /*!
9013  * \brief Makes given elements quadratic
9014  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9015  *  \param theElements - elements to make quadratic
9016  */
9017 //================================================================================
9018
9019 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9020                                           TIDSortedElemSet& theElements,
9021                                           const bool        theToBiQuad)
9022 {
9023   if ( theElements.empty() ) return;
9024
9025   // we believe that all theElements are of the same type
9026   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9027
9028   // get all nodes shared by theElements
9029   TIDSortedNodeSet allNodes;
9030   TIDSortedElemSet::iterator eIt = theElements.begin();
9031   for ( ; eIt != theElements.end(); ++eIt )
9032     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9033
9034   // complete theElements with elements of lower dim whose all nodes are in allNodes
9035
9036   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9037   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9038   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9039   for ( ; nIt != allNodes.end(); ++nIt )
9040   {
9041     const SMDS_MeshNode* n = *nIt;
9042     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9043     while ( invIt->more() )
9044     {
9045       const SMDS_MeshElement*      e = invIt->next();
9046       const SMDSAbs_ElementType type = e->GetType();
9047       if ( e->IsQuadratic() )
9048       {
9049         quadAdjacentElems[ type ].insert( e );
9050
9051         bool alreadyOK;
9052         switch ( e->GetEntityType() ) {
9053         case SMDSEntity_Quad_Triangle:
9054         case SMDSEntity_Quad_Quadrangle:
9055         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9056         case SMDSEntity_BiQuad_Triangle:
9057         case SMDSEntity_BiQuad_Quadrangle:
9058         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9059         default:                           alreadyOK = true;
9060         }
9061         if ( alreadyOK )
9062           continue;
9063       }
9064       if ( type >= elemType )
9065         continue; // same type or more complex linear element
9066
9067       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9068         continue; // e is already checked
9069
9070       // check nodes
9071       bool allIn = true;
9072       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9073       while ( nodeIt->more() && allIn )
9074         allIn = allNodes.count( nodeIt->next() );
9075       if ( allIn )
9076         theElements.insert(e );
9077     }
9078   }
9079
9080   SMESH_MesherHelper helper(*myMesh);
9081   helper.SetIsQuadratic( true );
9082   helper.SetIsBiQuadratic( theToBiQuad );
9083
9084   // add links of quadratic adjacent elements to the helper
9085
9086   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9087     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9088           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9089     {
9090       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9091     }
9092   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9093     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9094           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9095     {
9096       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9097     }
9098   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9099     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9100           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9101     {
9102       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9103     }
9104
9105   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9106
9107   SMESHDS_Mesh*  meshDS = GetMeshDS();
9108   SMESHDS_SubMesh* smDS = 0;
9109   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9110   {
9111     const SMDS_MeshElement* elem = *eIt;
9112
9113     bool alreadyOK;
9114     int nbCentralNodes = 0;
9115     switch ( elem->GetEntityType() ) {
9116       // linear convertible
9117     case SMDSEntity_Edge:
9118     case SMDSEntity_Triangle:
9119     case SMDSEntity_Quadrangle:
9120     case SMDSEntity_Tetra:
9121     case SMDSEntity_Pyramid:
9122     case SMDSEntity_Hexa:
9123     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9124       // quadratic that can become bi-quadratic
9125     case SMDSEntity_Quad_Triangle:
9126     case SMDSEntity_Quad_Quadrangle:
9127     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9128       // bi-quadratic
9129     case SMDSEntity_BiQuad_Triangle:
9130     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9131     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9132       // the rest
9133     default:                           alreadyOK = true;
9134     }
9135     if ( alreadyOK ) continue;
9136
9137     const SMDSAbs_ElementType type = elem->GetType();
9138     const int                   id = elem->GetID();
9139     const int              nbNodes = elem->NbCornerNodes();
9140     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9141
9142     helper.SetSubShape( elem->getshapeId() );
9143
9144     if ( !smDS || !smDS->Contains( elem ))
9145       smDS = meshDS->MeshElements( elem->getshapeId() );
9146     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9147
9148     SMDS_MeshElement * newElem = 0;
9149     switch( nbNodes )
9150     {
9151     case 4: // cases for most frequently used element types go first (for optimization)
9152       if ( type == SMDSAbs_Volume )
9153         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9154       else
9155         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9156       break;
9157     case 8:
9158       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9159                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9160       break;
9161     case 3:
9162       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9163       break;
9164     case 2:
9165       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9166       break;
9167     case 5:
9168       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9169                                  nodes[4], id, theForce3d);
9170       break;
9171     case 6:
9172       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9173                                  nodes[4], nodes[5], id, theForce3d);
9174       break;
9175     default:;
9176     }
9177     ReplaceElemInGroups( elem, newElem, meshDS);
9178     if( newElem && smDS )
9179       smDS->AddElement( newElem );
9180
9181     // remove central nodes
9182     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9183       if ( nodes[i]->NbInverseElements() == 0 )
9184         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9185
9186   } // loop on theElements
9187
9188   if ( !theForce3d )
9189   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9190     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9191     // helper.FixQuadraticElements( myError );
9192     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9193   }
9194 }
9195
9196 //=======================================================================
9197 /*!
9198  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9199  * \return int - nb of checked elements
9200  */
9201 //=======================================================================
9202
9203 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9204                                      SMDS_ElemIteratorPtr theItr,
9205                                      const int            theShapeID)
9206 {
9207   int nbElem = 0;
9208   SMESHDS_Mesh* meshDS = GetMeshDS();
9209   ElemFeatures elemType;
9210   vector<const SMDS_MeshNode *> nodes;
9211
9212   while( theItr->more() )
9213   {
9214     const SMDS_MeshElement* elem = theItr->next();
9215     nbElem++;
9216     if( elem && elem->IsQuadratic())
9217     {
9218       // get elem data
9219       int nbCornerNodes = elem->NbCornerNodes();
9220       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9221
9222       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9223
9224       //remove a quadratic element
9225       if ( !theSm || !theSm->Contains( elem ))
9226         theSm = meshDS->MeshElements( elem->getshapeId() );
9227       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9228
9229       // remove medium nodes
9230       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9231         if ( nodes[i]->NbInverseElements() == 0 )
9232           meshDS->RemoveFreeNode( nodes[i], theSm );
9233
9234       // add a linear element
9235       nodes.resize( nbCornerNodes );
9236       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9237       ReplaceElemInGroups(elem, newElem, meshDS);
9238       if( theSm && newElem )
9239         theSm->AddElement( newElem );
9240     }
9241   }
9242   return nbElem;
9243 }
9244
9245 //=======================================================================
9246 //function : ConvertFromQuadratic
9247 //purpose  :
9248 //=======================================================================
9249
9250 bool SMESH_MeshEditor::ConvertFromQuadratic()
9251 {
9252   int nbCheckedElems = 0;
9253   if ( myMesh->HasShapeToMesh() )
9254   {
9255     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9256     {
9257       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9258       while ( smIt->more() ) {
9259         SMESH_subMesh* sm = smIt->next();
9260         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9261           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9262       }
9263     }
9264   }
9265
9266   int totalNbElems =
9267     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9268   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9269   {
9270     SMESHDS_SubMesh *aSM = 0;
9271     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9272   }
9273
9274   return true;
9275 }
9276
9277 namespace
9278 {
9279   //================================================================================
9280   /*!
9281    * \brief Return true if all medium nodes of the element are in the node set
9282    */
9283   //================================================================================
9284
9285   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9286   {
9287     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9288       if ( !nodeSet.count( elem->GetNode(i) ))
9289         return false;
9290     return true;
9291   }
9292 }
9293
9294 //================================================================================
9295 /*!
9296  * \brief Makes given elements linear
9297  */
9298 //================================================================================
9299
9300 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9301 {
9302   if ( theElements.empty() ) return;
9303
9304   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9305   set<int> mediumNodeIDs;
9306   TIDSortedElemSet::iterator eIt = theElements.begin();
9307   for ( ; eIt != theElements.end(); ++eIt )
9308   {
9309     const SMDS_MeshElement* e = *eIt;
9310     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9311       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9312   }
9313
9314   // replace given elements by linear ones
9315   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9316   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9317
9318   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9319   // except those elements sharing medium nodes of quadratic element whose medium nodes
9320   // are not all in mediumNodeIDs
9321
9322   // get remaining medium nodes
9323   TIDSortedNodeSet mediumNodes;
9324   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9325   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9326     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9327       mediumNodes.insert( mediumNodes.end(), n );
9328
9329   // find more quadratic elements to convert
9330   TIDSortedElemSet moreElemsToConvert;
9331   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9332   for ( ; nIt != mediumNodes.end(); ++nIt )
9333   {
9334     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9335     while ( invIt->more() )
9336     {
9337       const SMDS_MeshElement* e = invIt->next();
9338       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9339       {
9340         // find a more complex element including e and
9341         // whose medium nodes are not in mediumNodes
9342         bool complexFound = false;
9343         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9344         {
9345           SMDS_ElemIteratorPtr invIt2 =
9346             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9347           while ( invIt2->more() )
9348           {
9349             const SMDS_MeshElement* eComplex = invIt2->next();
9350             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9351             {
9352               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9353               if ( nbCommonNodes == e->NbNodes())
9354               {
9355                 complexFound = true;
9356                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9357                 break;
9358               }
9359             }
9360           }
9361         }
9362         if ( !complexFound )
9363           moreElemsToConvert.insert( e );
9364       }
9365     }
9366   }
9367   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9368   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9369 }
9370
9371 //=======================================================================
9372 //function : SewSideElements
9373 //purpose  :
9374 //=======================================================================
9375
9376 SMESH_MeshEditor::Sew_Error
9377 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9378                                    TIDSortedElemSet&    theSide2,
9379                                    const SMDS_MeshNode* theFirstNode1,
9380                                    const SMDS_MeshNode* theFirstNode2,
9381                                    const SMDS_MeshNode* theSecondNode1,
9382                                    const SMDS_MeshNode* theSecondNode2)
9383 {
9384   ClearLastCreated();
9385
9386   if ( theSide1.size() != theSide2.size() )
9387     return SEW_DIFF_NB_OF_ELEMENTS;
9388
9389   Sew_Error aResult = SEW_OK;
9390   // Algo:
9391   // 1. Build set of faces representing each side
9392   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9393   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9394
9395   // =======================================================================
9396   // 1. Build set of faces representing each side:
9397   // =======================================================================
9398   // a. build set of nodes belonging to faces
9399   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9400   // c. create temporary faces representing side of volumes if correspondent
9401   //    face does not exist
9402
9403   SMESHDS_Mesh* aMesh = GetMeshDS();
9404   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9405   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9406   TIDSortedElemSet             faceSet1, faceSet2;
9407   set<const SMDS_MeshElement*> volSet1,  volSet2;
9408   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9409   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9410   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9411   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9412   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9413   int iSide, iFace, iNode;
9414
9415   list<const SMDS_MeshElement* > tempFaceList;
9416   for ( iSide = 0; iSide < 2; iSide++ ) {
9417     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9418     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9419     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9420     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9421     set<const SMDS_MeshElement*>::iterator vIt;
9422     TIDSortedElemSet::iterator eIt;
9423     set<const SMDS_MeshNode*>::iterator    nIt;
9424
9425     // check that given nodes belong to given elements
9426     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9427     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9428     int firstIndex = -1, secondIndex = -1;
9429     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9430       const SMDS_MeshElement* elem = *eIt;
9431       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9432       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9433       if ( firstIndex > -1 && secondIndex > -1 ) break;
9434     }
9435     if ( firstIndex < 0 || secondIndex < 0 ) {
9436       // we can simply return until temporary faces created
9437       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9438     }
9439
9440     // -----------------------------------------------------------
9441     // 1a. Collect nodes of existing faces
9442     //     and build set of face nodes in order to detect missing
9443     //     faces corresponding to sides of volumes
9444     // -----------------------------------------------------------
9445
9446     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9447
9448     // loop on the given element of a side
9449     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9450       //const SMDS_MeshElement* elem = *eIt;
9451       const SMDS_MeshElement* elem = *eIt;
9452       if ( elem->GetType() == SMDSAbs_Face ) {
9453         faceSet->insert( elem );
9454         set <const SMDS_MeshNode*> faceNodeSet;
9455         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9456         while ( nodeIt->more() ) {
9457           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9458           nodeSet->insert( n );
9459           faceNodeSet.insert( n );
9460         }
9461         setOfFaceNodeSet.insert( faceNodeSet );
9462       }
9463       else if ( elem->GetType() == SMDSAbs_Volume )
9464         volSet->insert( elem );
9465     }
9466     // ------------------------------------------------------------------------------
9467     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9468     // ------------------------------------------------------------------------------
9469
9470     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9471       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9472       while ( fIt->more() ) { // loop on faces sharing a node
9473         const SMDS_MeshElement* f = fIt->next();
9474         if ( faceSet->find( f ) == faceSet->end() ) {
9475           // check if all nodes are in nodeSet and
9476           // complete setOfFaceNodeSet if they are
9477           set <const SMDS_MeshNode*> faceNodeSet;
9478           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9479           bool allInSet = true;
9480           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9481             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9482             if ( nodeSet->find( n ) == nodeSet->end() )
9483               allInSet = false;
9484             else
9485               faceNodeSet.insert( n );
9486           }
9487           if ( allInSet ) {
9488             faceSet->insert( f );
9489             setOfFaceNodeSet.insert( faceNodeSet );
9490           }
9491         }
9492       }
9493     }
9494
9495     // -------------------------------------------------------------------------
9496     // 1c. Create temporary faces representing sides of volumes if correspondent
9497     //     face does not exist
9498     // -------------------------------------------------------------------------
9499
9500     if ( !volSet->empty() ) {
9501       //int nodeSetSize = nodeSet->size();
9502
9503       // loop on given volumes
9504       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9505         SMDS_VolumeTool vol (*vIt);
9506         // loop on volume faces: find free faces
9507         // --------------------------------------
9508         list<const SMDS_MeshElement* > freeFaceList;
9509         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9510           if ( !vol.IsFreeFace( iFace ))
9511             continue;
9512           // check if there is already a face with same nodes in a face set
9513           const SMDS_MeshElement* aFreeFace = 0;
9514           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9515           int nbNodes = vol.NbFaceNodes( iFace );
9516           set <const SMDS_MeshNode*> faceNodeSet;
9517           vol.GetFaceNodes( iFace, faceNodeSet );
9518           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9519           if ( isNewFace ) {
9520             // no such a face is given but it still can exist, check it
9521             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9522             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9523           }
9524           if ( !aFreeFace ) {
9525             // create a temporary face
9526             if ( nbNodes == 3 ) {
9527               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9528               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9529             }
9530             else if ( nbNodes == 4 ) {
9531               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9532               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9533             }
9534             else {
9535               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9536               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9537               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9538             }
9539             if ( aFreeFace )
9540               tempFaceList.push_back( aFreeFace );
9541           }
9542
9543           if ( aFreeFace )
9544             freeFaceList.push_back( aFreeFace );
9545
9546         } // loop on faces of a volume
9547
9548         // choose one of several free faces of a volume
9549         // --------------------------------------------
9550         if ( freeFaceList.size() > 1 ) {
9551           // choose a face having max nb of nodes shared by other elems of a side
9552           int maxNbNodes = -1;
9553           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9554           while ( fIt != freeFaceList.end() ) { // loop on free faces
9555             int nbSharedNodes = 0;
9556             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9557             while ( nodeIt->more() ) { // loop on free face nodes
9558               const SMDS_MeshNode* n =
9559                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9561               while ( invElemIt->more() ) {
9562                 const SMDS_MeshElement* e = invElemIt->next();
9563                 nbSharedNodes += faceSet->count( e );
9564                 nbSharedNodes += elemSet->count( e );
9565               }
9566             }
9567             if ( nbSharedNodes > maxNbNodes ) {
9568               maxNbNodes = nbSharedNodes;
9569               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9570             }
9571             else if ( nbSharedNodes == maxNbNodes ) {
9572               fIt++;
9573             }
9574             else {
9575               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9576             }
9577           }
9578           if ( freeFaceList.size() > 1 )
9579           {
9580             // could not choose one face, use another way
9581             // choose a face most close to the bary center of the opposite side
9582             gp_XYZ aBC( 0., 0., 0. );
9583             set <const SMDS_MeshNode*> addedNodes;
9584             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9585             eIt = elemSet2->begin();
9586             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9587               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9588               while ( nodeIt->more() ) { // loop on free face nodes
9589                 const SMDS_MeshNode* n =
9590                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591                 if ( addedNodes.insert( n ).second )
9592                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9593               }
9594             }
9595             aBC /= addedNodes.size();
9596             double minDist = DBL_MAX;
9597             fIt = freeFaceList.begin();
9598             while ( fIt != freeFaceList.end() ) { // loop on free faces
9599               double dist = 0;
9600               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9601               while ( nodeIt->more() ) { // loop on free face nodes
9602                 const SMDS_MeshNode* n =
9603                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9604                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9605                 dist += ( aBC - p ).SquareModulus();
9606               }
9607               if ( dist < minDist ) {
9608                 minDist = dist;
9609                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9610               }
9611               else
9612                 fIt = freeFaceList.erase( fIt++ );
9613             }
9614           }
9615         } // choose one of several free faces of a volume
9616
9617         if ( freeFaceList.size() == 1 ) {
9618           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9619           faceSet->insert( aFreeFace );
9620           // complete a node set with nodes of a found free face
9621           //           for ( iNode = 0; iNode < ; iNode++ )
9622           //             nodeSet->insert( fNodes[ iNode ] );
9623         }
9624
9625       } // loop on volumes of a side
9626
9627       //       // complete a set of faces if new nodes in a nodeSet appeared
9628       //       // ----------------------------------------------------------
9629       //       if ( nodeSetSize != nodeSet->size() ) {
9630       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9631       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9632       //           while ( fIt->more() ) { // loop on faces sharing a node
9633       //             const SMDS_MeshElement* f = fIt->next();
9634       //             if ( faceSet->find( f ) == faceSet->end() ) {
9635       //               // check if all nodes are in nodeSet and
9636       //               // complete setOfFaceNodeSet if they are
9637       //               set <const SMDS_MeshNode*> faceNodeSet;
9638       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9639       //               bool allInSet = true;
9640       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9641       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9642       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9643       //                   allInSet = false;
9644       //                 else
9645       //                   faceNodeSet.insert( n );
9646       //               }
9647       //               if ( allInSet ) {
9648       //                 faceSet->insert( f );
9649       //                 setOfFaceNodeSet.insert( faceNodeSet );
9650       //               }
9651       //             }
9652       //           }
9653       //         }
9654       //       }
9655     } // Create temporary faces, if there are volumes given
9656   } // loop on sides
9657
9658   if ( faceSet1.size() != faceSet2.size() ) {
9659     // delete temporary faces: they are in reverseElements of actual nodes
9660     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9661     //    while ( tmpFaceIt->more() )
9662     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9663     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9664     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9665     //      aMesh->RemoveElement(*tmpFaceIt);
9666     MESSAGE("Diff nb of faces");
9667     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9668   }
9669
9670   // ============================================================
9671   // 2. Find nodes to merge:
9672   //              bind a node to remove to a node to put instead
9673   // ============================================================
9674
9675   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9676   if ( theFirstNode1 != theFirstNode2 )
9677     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9678   if ( theSecondNode1 != theSecondNode2 )
9679     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9680
9681   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9682   set< long > linkIdSet; // links to process
9683   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9684
9685   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9686   list< NLink > linkList[2];
9687   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9688   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9689   // loop on links in linkList; find faces by links and append links
9690   // of the found faces to linkList
9691   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9692   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9693   {
9694     NLink link[] = { *linkIt[0], *linkIt[1] };
9695     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9696     if ( !linkIdSet.count( linkID ) )
9697       continue;
9698
9699     // by links, find faces in the face sets,
9700     // and find indices of link nodes in the found faces;
9701     // in a face set, there is only one or no face sharing a link
9702     // ---------------------------------------------------------------
9703
9704     const SMDS_MeshElement* face[] = { 0, 0 };
9705     vector<const SMDS_MeshNode*> fnodes[2];
9706     int iLinkNode[2][2];
9707     TIDSortedElemSet avoidSet;
9708     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9709       const SMDS_MeshNode* n1 = link[iSide].first;
9710       const SMDS_MeshNode* n2 = link[iSide].second;
9711       //cout << "Side " << iSide << " ";
9712       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9713       // find a face by two link nodes
9714       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9715                                                       *faceSetPtr[ iSide ], avoidSet,
9716                                                       &iLinkNode[iSide][0],
9717                                                       &iLinkNode[iSide][1] );
9718       if ( face[ iSide ])
9719       {
9720         //cout << " F " << face[ iSide]->GetID() <<endl;
9721         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9722         // put face nodes to fnodes
9723         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9724         fnodes[ iSide ].assign( nIt, nEnd );
9725         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9726       }
9727     }
9728
9729     // check similarity of elements of the sides
9730     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9731       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9732       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9733         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9734       }
9735       else {
9736         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9737       }
9738       break; // do not return because it's necessary to remove tmp faces
9739     }
9740
9741     // set nodes to merge
9742     // -------------------
9743
9744     if ( face[0] && face[1] )  {
9745       const int nbNodes = face[0]->NbNodes();
9746       if ( nbNodes != face[1]->NbNodes() ) {
9747         MESSAGE("Diff nb of face nodes");
9748         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9749         break; // do not return because it s necessary to remove tmp faces
9750       }
9751       bool reverse[] = { false, false }; // order of nodes in the link
9752       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9753         // analyse link orientation in faces
9754         int i1 = iLinkNode[ iSide ][ 0 ];
9755         int i2 = iLinkNode[ iSide ][ 1 ];
9756         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9757       }
9758       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9759       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9760       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9761       {
9762         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9763                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9764       }
9765
9766       // add other links of the faces to linkList
9767       // -----------------------------------------
9768
9769       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9770         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9771         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9772         if ( !iter_isnew.second ) { // already in a set: no need to process
9773           linkIdSet.erase( iter_isnew.first );
9774         }
9775         else // new in set == encountered for the first time: add
9776         {
9777           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9778           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9779           linkList[0].push_back ( NLink( n1, n2 ));
9780           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9781         }
9782       }
9783     } // 2 faces found
9784
9785     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9786       break;
9787
9788   } // loop on link lists
9789
9790   if ( aResult == SEW_OK &&
9791        ( //linkIt[0] != linkList[0].end() ||
9792         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9793     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9794              " " << (faceSetPtr[1]->empty()));
9795     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9796   }
9797
9798   // ====================================================================
9799   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9800   // ====================================================================
9801
9802   // delete temporary faces
9803   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9804   //  while ( tmpFaceIt->more() )
9805   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9806   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9807   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9808     aMesh->RemoveElement(*tmpFaceIt);
9809
9810   if ( aResult != SEW_OK)
9811     return aResult;
9812
9813   list< int > nodeIDsToRemove;
9814   vector< const SMDS_MeshNode*> nodes;
9815   ElemFeatures elemType;
9816
9817   // loop on nodes replacement map
9818   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9819   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9820     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9821     {
9822       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9823       nodeIDsToRemove.push_back( nToRemove->GetID() );
9824       // loop on elements sharing nToRemove
9825       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9826       while ( invElemIt->more() ) {
9827         const SMDS_MeshElement* e = invElemIt->next();
9828         // get a new suite of nodes: make replacement
9829         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9830         nodes.resize( nbNodes );
9831         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9832         while ( nIt->more() ) {
9833           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9834           nnIt = nReplaceMap.find( n );
9835           if ( nnIt != nReplaceMap.end() ) {
9836             nbReplaced++;
9837             n = (*nnIt).second;
9838           }
9839           nodes[ i++ ] = n;
9840         }
9841         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9842         //         elemIDsToRemove.push_back( e->GetID() );
9843         //       else
9844         if ( nbReplaced )
9845         {
9846           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9847           aMesh->RemoveElement( e );
9848
9849           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9850           {
9851             AddToSameGroups( newElem, e, aMesh );
9852             if ( int aShapeId = e->getshapeId() )
9853               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9854           }
9855         }
9856       }
9857     }
9858
9859   Remove( nodeIDsToRemove, true );
9860
9861   return aResult;
9862 }
9863
9864 //================================================================================
9865 /*!
9866  * \brief Find corresponding nodes in two sets of faces
9867  * \param theSide1 - first face set
9868  * \param theSide2 - second first face
9869  * \param theFirstNode1 - a boundary node of set 1
9870  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9871  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9872  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9873  * \param nReplaceMap - output map of corresponding nodes
9874  * \return bool  - is a success or not
9875  */
9876 //================================================================================
9877
9878 #ifdef _DEBUG_
9879 //#define DEBUG_MATCHING_NODES
9880 #endif
9881
9882 SMESH_MeshEditor::Sew_Error
9883 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9884                                     set<const SMDS_MeshElement*>& theSide2,
9885                                     const SMDS_MeshNode*          theFirstNode1,
9886                                     const SMDS_MeshNode*          theFirstNode2,
9887                                     const SMDS_MeshNode*          theSecondNode1,
9888                                     const SMDS_MeshNode*          theSecondNode2,
9889                                     TNodeNodeMap &                nReplaceMap)
9890 {
9891   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9892
9893   nReplaceMap.clear();
9894   //if ( theFirstNode1 != theFirstNode2 )
9895   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9896   //if ( theSecondNode1 != theSecondNode2 )
9897   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9898
9899   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9900   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9901
9902   list< NLink > linkList[2];
9903   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9904   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9905
9906   // loop on links in linkList; find faces by links and append links
9907   // of the found faces to linkList
9908   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9909   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9910     NLink link[] = { *linkIt[0], *linkIt[1] };
9911     if ( linkSet.find( link[0] ) == linkSet.end() )
9912       continue;
9913
9914     // by links, find faces in the face sets,
9915     // and find indices of link nodes in the found faces;
9916     // in a face set, there is only one or no face sharing a link
9917     // ---------------------------------------------------------------
9918
9919     const SMDS_MeshElement* face[] = { 0, 0 };
9920     list<const SMDS_MeshNode*> notLinkNodes[2];
9921     //bool reverse[] = { false, false }; // order of notLinkNodes
9922     int nbNodes[2];
9923     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9924     {
9925       const SMDS_MeshNode* n1 = link[iSide].first;
9926       const SMDS_MeshNode* n2 = link[iSide].second;
9927       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9928       set< const SMDS_MeshElement* > facesOfNode1;
9929       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9930       {
9931         // during a loop of the first node, we find all faces around n1,
9932         // during a loop of the second node, we find one face sharing both n1 and n2
9933         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9934         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9935         while ( fIt->more() ) { // loop on faces sharing a node
9936           const SMDS_MeshElement* f = fIt->next();
9937           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9938               ! facesOfNode1.insert( f ).second ) // f encounters twice
9939           {
9940             if ( face[ iSide ] ) {
9941               MESSAGE( "2 faces per link " );
9942               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9943             }
9944             face[ iSide ] = f;
9945             faceSet->erase( f );
9946
9947             // get not link nodes
9948             int nbN = f->NbNodes();
9949             if ( f->IsQuadratic() )
9950               nbN /= 2;
9951             nbNodes[ iSide ] = nbN;
9952             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9953             int i1 = f->GetNodeIndex( n1 );
9954             int i2 = f->GetNodeIndex( n2 );
9955             int iEnd = nbN, iBeg = -1, iDelta = 1;
9956             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9957             if ( reverse ) {
9958               std::swap( iEnd, iBeg ); iDelta = -1;
9959             }
9960             int i = i2;
9961             while ( true ) {
9962               i += iDelta;
9963               if ( i == iEnd ) i = iBeg + iDelta;
9964               if ( i == i1 ) break;
9965               nodes.push_back ( f->GetNode( i ) );
9966             }
9967           }
9968         }
9969       }
9970     }
9971     // check similarity of elements of the sides
9972     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9973       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9974       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9975         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9976       }
9977       else {
9978         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9979       }
9980     }
9981
9982     // set nodes to merge
9983     // -------------------
9984
9985     if ( face[0] && face[1] )  {
9986       if ( nbNodes[0] != nbNodes[1] ) {
9987         MESSAGE("Diff nb of face nodes");
9988         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9989       }
9990 #ifdef DEBUG_MATCHING_NODES
9991       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9992                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9993                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9994 #endif
9995       int nbN = nbNodes[0];
9996       {
9997         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9998         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9999         for ( int i = 0 ; i < nbN - 2; ++i ) {
10000 #ifdef DEBUG_MATCHING_NODES
10001           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10002 #endif
10003           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10004         }
10005       }
10006
10007       // add other links of the face 1 to linkList
10008       // -----------------------------------------
10009
10010       const SMDS_MeshElement* f0 = face[0];
10011       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10012       for ( int i = 0; i < nbN; i++ )
10013       {
10014         const SMDS_MeshNode* n2 = f0->GetNode( i );
10015         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10016           linkSet.insert( SMESH_TLink( n1, n2 ));
10017         if ( !iter_isnew.second ) { // already in a set: no need to process
10018           linkSet.erase( iter_isnew.first );
10019         }
10020         else // new in set == encountered for the first time: add
10021         {
10022 #ifdef DEBUG_MATCHING_NODES
10023           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10024                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10025 #endif
10026           linkList[0].push_back ( NLink( n1, n2 ));
10027           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10028         }
10029         n1 = n2;
10030       }
10031     } // 2 faces found
10032   } // loop on link lists
10033
10034   return SEW_OK;
10035 }
10036
10037 namespace // automatically find theAffectedElems for DoubleNodes()
10038 {
10039   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10040
10041   //--------------------------------------------------------------------------------
10042   // Nodes shared by adjacent FissureBorder's.
10043   // 1 node  if FissureBorder separates faces
10044   // 2 nodes if FissureBorder separates volumes
10045   struct SubBorder
10046   {
10047     const SMDS_MeshNode* _nodes[2];
10048     int                  _nbNodes;
10049
10050     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10051     {
10052       _nodes[0] = n1;
10053       _nodes[1] = n2;
10054       _nbNodes = bool( n1 ) + bool( n2 );
10055       if ( _nbNodes == 2 && n1 > n2 )
10056         std::swap( _nodes[0], _nodes[1] );
10057     }
10058     bool operator<( const SubBorder& other ) const
10059     {
10060       for ( int i = 0; i < _nbNodes; ++i )
10061       {
10062         if ( _nodes[i] < other._nodes[i] ) return true;
10063         if ( _nodes[i] > other._nodes[i] ) return false;
10064       }
10065       return false;
10066     }
10067   };
10068
10069   //--------------------------------------------------------------------------------
10070   // Map a SubBorder to all FissureBorder it bounds
10071   struct FissureBorder;
10072   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10073   typedef TBorderLinks::iterator                               TMappedSub;
10074
10075   //--------------------------------------------------------------------------------
10076   /*!
10077    * \brief Element border (volume facet or face edge) at a fissure
10078    */
10079   struct FissureBorder
10080   {
10081     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10082     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10083
10084     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10085     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10086
10087     FissureBorder( FissureBorder && from ) // move constructor
10088     {
10089       std::swap( _nodes,       from._nodes );
10090       std::swap( _sortedNodes, from._sortedNodes );
10091       _elems[0] = from._elems[0];
10092       _elems[1] = from._elems[1];
10093     }
10094
10095     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10096                    std::vector< const SMDS_MeshElement* > & adjElems)
10097       : _nodes( elemToDuplicate->NbCornerNodes() )
10098     {
10099       for ( size_t i = 0; i < _nodes.size(); ++i )
10100         _nodes[i] = elemToDuplicate->GetNode( i );
10101
10102       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10103       findAdjacent( type, adjElems );
10104     }
10105
10106     FissureBorder( const SMDS_MeshNode**                    nodes,
10107                    const size_t                             nbNodes,
10108                    const SMDSAbs_ElementType                adjElemsType,
10109                    std::vector< const SMDS_MeshElement* > & adjElems)
10110       : _nodes( nodes, nodes + nbNodes )
10111     {
10112       findAdjacent( adjElemsType, adjElems );
10113     }
10114
10115     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10116                        std::vector< const SMDS_MeshElement* > & adjElems)
10117     {
10118       _elems[0] = _elems[1] = 0;
10119       adjElems.clear();
10120       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10121         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10122           _elems[i] = adjElems[i];
10123     }
10124
10125     bool operator<( const FissureBorder& other ) const
10126     {
10127       return GetSortedNodes() < other.GetSortedNodes();
10128     }
10129
10130     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10131     {
10132       if ( _sortedNodes.empty() && !_nodes.empty() )
10133       {
10134         FissureBorder* me = const_cast<FissureBorder*>( this );
10135         me->_sortedNodes = me->_nodes;
10136         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10137       }
10138       return _sortedNodes;
10139     }
10140
10141     size_t NbSub() const
10142     {
10143       return _nodes.size();
10144     }
10145
10146     SubBorder Sub(size_t i) const
10147     {
10148       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10149     }
10150
10151     void AddSelfTo( TBorderLinks& borderLinks )
10152     {
10153       _mappedSubs.resize( NbSub() );
10154       for ( size_t i = 0; i < NbSub(); ++i )
10155       {
10156         TBorderLinks::iterator s2b =
10157           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10158         s2b->second.push_back( this );
10159         _mappedSubs[ i ] = s2b;
10160       }
10161     }
10162
10163     void Clear()
10164     {
10165       _nodes.clear();
10166     }
10167
10168     const SMDS_MeshElement* GetMarkedElem() const
10169     {
10170       if ( _nodes.empty() ) return 0; // cleared
10171       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10172       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10173       return 0;
10174     }
10175
10176     gp_XYZ GetNorm() const // normal to the border
10177     {
10178       gp_XYZ norm;
10179       if ( _nodes.size() == 2 )
10180       {
10181         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10182         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10183           avgNorm += norm;
10184         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10185           avgNorm += norm;
10186
10187         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10188         norm = bordDir ^ avgNorm;
10189       }
10190       else
10191       {
10192         SMESH_NodeXYZ p0( _nodes[0] );
10193         SMESH_NodeXYZ p1( _nodes[1] );
10194         SMESH_NodeXYZ p2( _nodes[2] );
10195         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10196       }
10197       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10198         norm.Reverse();
10199
10200       return norm;
10201     }
10202
10203     void ChooseSide() // mark an _elem located at positive side of fissure
10204     {
10205       _elems[0]->setIsMarked( true );
10206       gp_XYZ norm = GetNorm();
10207       double maxX = norm.Coord(1);
10208       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10209       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10210       if ( maxX < 0 )
10211       {
10212         _elems[0]->setIsMarked( false );
10213         _elems[1]->setIsMarked( true );
10214       }
10215     }
10216
10217   }; // struct FissureBorder
10218
10219   //--------------------------------------------------------------------------------
10220   /*!
10221    * \brief Classifier of elements at fissure edge
10222    */
10223   class FissureNormal
10224   {
10225     std::vector< gp_XYZ > _normals;
10226     bool                  _bothIn;
10227
10228   public:
10229     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10230     {
10231       _bothIn = false;
10232       _normals.reserve(2);
10233       _normals.push_back( bord.GetNorm() );
10234       if ( _normals.size() == 2 )
10235         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10236     }
10237
10238     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10239     {
10240       bool isIn = false;
10241       switch ( _normals.size() ) {
10242       case 1:
10243       {
10244         isIn = !isOut( n, _normals[0], elem );
10245         break;
10246       }
10247       case 2:
10248       {
10249         bool in1 = !isOut( n, _normals[0], elem );
10250         bool in2 = !isOut( n, _normals[1], elem );
10251         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10252       }
10253       }
10254       return isIn;
10255     }
10256   };
10257
10258   //================================================================================
10259   /*!
10260    * \brief Classify an element by a plane passing through a node
10261    */
10262   //================================================================================
10263
10264   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10265   {
10266     SMESH_NodeXYZ p = n;
10267     double sumDot = 0;
10268     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10269     {
10270       SMESH_NodeXYZ pi = elem->GetNode( i );
10271       sumDot += norm * ( pi - p );
10272     }
10273     return sumDot < -1e-100;
10274   }
10275
10276   //================================================================================
10277   /*!
10278    * \brief Find FissureBorder's by nodes to duplicate
10279    */
10280   //================================================================================
10281
10282   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10283                            std::vector< FissureBorder > & theFissureBorders )
10284   {
10285     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10286     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10287     if ( !n ) return;
10288     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10289     if ( n->NbInverseElements( elemType ) == 0 )
10290     {
10291       elemType = SMDSAbs_Face;
10292       if ( n->NbInverseElements( elemType ) == 0 )
10293         return;
10294     }
10295     // unmark elements touching the fissure
10296     for ( ; nIt != theNodes.end(); ++nIt )
10297       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10298
10299     // loop on elements touching the fissure to get their borders belonging to the fissure
10300     std::set< FissureBorder >              fissureBorders;
10301     std::vector< const SMDS_MeshElement* > adjElems;
10302     std::vector< const SMDS_MeshNode* >    nodes;
10303     SMDS_VolumeTool volTool;
10304     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10305     {
10306       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10307       while ( invIt->more() )
10308       {
10309         const SMDS_MeshElement* eInv = invIt->next();
10310         if ( eInv->isMarked() ) continue;
10311         eInv->setIsMarked( true );
10312
10313         if ( elemType == SMDSAbs_Volume )
10314         {
10315           volTool.Set( eInv );
10316           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10317           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10318           {
10319             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10320             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10321             nodes.clear();
10322             bool allOnFissure = true;
10323             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10324               if (( allOnFissure = theNodes.count( nn[ iN ])))
10325                 nodes.push_back( nn[ iN ]);
10326             if ( allOnFissure )
10327               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10328                                                                elemType, adjElems )));
10329           }
10330         }
10331         else // elemType == SMDSAbs_Face
10332         {
10333           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10334           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10335           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10336           {
10337             nn[1]      = eInv->GetNode( iN );
10338             onFissure1 = theNodes.count( nn[1] );
10339             if ( onFissure0 && onFissure1 )
10340               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10341             nn[0]      = nn[1];
10342             onFissure0 = onFissure1;
10343           }
10344         }
10345       }
10346     }
10347
10348     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10349     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10350     for ( ; bord != fissureBorders.end(); ++bord )
10351     {
10352       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10353     }
10354     return;
10355   } // findFissureBorders()
10356
10357   //================================================================================
10358   /*!
10359    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10360    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10361    *  \param [in] theNodesNot - nodes not to duplicate
10362    *  \param [out] theAffectedElems - the found elements
10363    */
10364   //================================================================================
10365
10366   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10367                           TIDSortedElemSet&       theAffectedElems)
10368   {
10369     if ( theElemsOrNodes.empty() ) return;
10370
10371     // find FissureBorder's
10372
10373     std::vector< FissureBorder >           fissure;
10374     std::vector< const SMDS_MeshElement* > elemsByFacet;
10375
10376     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10377     if ( (*elIt)->GetType() == SMDSAbs_Node )
10378     {
10379       findFissureBorders( theElemsOrNodes, fissure );
10380     }
10381     else
10382     {
10383       fissure.reserve( theElemsOrNodes.size() );
10384       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10385         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10386     }
10387     if ( fissure.empty() )
10388       return;
10389
10390     // fill borderLinks
10391
10392     TBorderLinks borderLinks;
10393
10394     for ( size_t i = 0; i < fissure.size(); ++i )
10395     {
10396       fissure[i].AddSelfTo( borderLinks );
10397     }
10398
10399     // get theAffectedElems
10400
10401     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10402     for ( size_t i = 0; i < fissure.size(); ++i )
10403       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10404       {
10405         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10406                                         false, /*markElem=*/true );
10407       }
10408
10409     std::vector<const SMDS_MeshNode *>                 facetNodes;
10410     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10411     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10412
10413     // choose a side of fissure
10414     fissure[0].ChooseSide();
10415     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10416
10417     size_t nbCheckedBorders = 0;
10418     while ( nbCheckedBorders < fissure.size() )
10419     {
10420       // find a FissureBorder to treat
10421       FissureBorder* bord = 0;
10422       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10423         if ( fissure[i].GetMarkedElem() )
10424           bord = & fissure[i];
10425       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10426         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10427         {
10428           bord = & fissure[i];
10429           bord->ChooseSide();
10430           theAffectedElems.insert( bord->GetMarkedElem() );
10431         }
10432       if ( !bord ) return;
10433       ++nbCheckedBorders;
10434
10435       // treat FissureBorder's linked to bord
10436       fissureNodes.clear();
10437       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10438       for ( size_t i = 0; i < bord->NbSub(); ++i )
10439       {
10440         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10441         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10442         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10443         const SubBorder&                          sb = l2b->first;
10444         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10445
10446         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10447         {
10448           for ( int j = 0; j < sb._nbNodes; ++j )
10449             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10450           continue;
10451         }
10452
10453         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10454         // until an elem adjacent to a neighbour FissureBorder is found
10455         facetNodes.clear();
10456         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10457         facetNodes.resize( sb._nbNodes + 1 );
10458
10459         while ( bordElem )
10460         {
10461           // check if bordElem is adjacent to a neighbour FissureBorder
10462           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10463           {
10464             FissureBorder* bord2 = linkedBorders[j];
10465             if ( bord2 == bord ) continue;
10466             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10467               bordElem = 0;
10468             else
10469               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10470           }
10471           if ( !bordElem )
10472             break;
10473
10474           // find the next bordElem
10475           const SMDS_MeshElement* nextBordElem = 0;
10476           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10477           {
10478             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10479             if ( fissureNodes.count( n )) continue;
10480
10481             facetNodes[ sb._nbNodes ] = n;
10482             elemsByFacet.clear();
10483             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10484             {
10485               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10486                 if ( elemsByFacet[ iE ] != bordElem &&
10487                      !elemsByFacet[ iE ]->isMarked() )
10488                 {
10489                   theAffectedElems.insert( elemsByFacet[ iE ]);
10490                   elemsByFacet[ iE ]->setIsMarked( true );
10491                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10492                     nextBordElem = elemsByFacet[ iE ];
10493                 }
10494             }
10495           }
10496           bordElem = nextBordElem;
10497
10498         } // while ( bordElem )
10499
10500         linkedBorders.clear(); // not to treat this link any more
10501
10502       } // loop on SubBorder's of a FissureBorder
10503
10504       bord->Clear();
10505
10506     } // loop on FissureBorder's
10507
10508
10509     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10510
10511     // mark nodes of theAffectedElems
10512     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10513
10514     // unmark nodes of the fissure
10515     elIt = theElemsOrNodes.begin();
10516     if ( (*elIt)->GetType() == SMDSAbs_Node )
10517       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10518     else
10519       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10520
10521     std::vector< gp_XYZ > normVec;
10522
10523     // loop on nodes of the fissure, add elements having marked nodes
10524     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10525     {
10526       const SMDS_MeshElement* e = (*elIt);
10527       if ( e->GetType() != SMDSAbs_Node )
10528         e->setIsMarked( true ); // avoid adding a fissure element
10529
10530       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10531       {
10532         const SMDS_MeshNode* n = e->GetNode( iN );
10533         if ( fissEdgeNodes2Norm.count( n ))
10534           continue;
10535
10536         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10537         while ( invIt->more() )
10538         {
10539           const SMDS_MeshElement* eInv = invIt->next();
10540           if ( eInv->isMarked() ) continue;
10541           eInv->setIsMarked( true );
10542
10543           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10544           while( nIt->more() )
10545             if ( nIt->next()->isMarked())
10546             {
10547               theAffectedElems.insert( eInv );
10548               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10549               n->setIsMarked( false );
10550               break;
10551             }
10552         }
10553       }
10554     }
10555
10556     // add elements on the fissure edge
10557     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10558     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10559     {
10560       const SMDS_MeshNode* edgeNode = n2N->first;
10561       const FissureNormal & normals = n2N->second;
10562
10563       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10564       while ( invIt->more() )
10565       {
10566         const SMDS_MeshElement* eInv = invIt->next();
10567         if ( eInv->isMarked() ) continue;
10568         eInv->setIsMarked( true );
10569
10570         // classify eInv using normals
10571         bool toAdd = normals.IsIn( edgeNode, eInv );
10572         if ( toAdd ) // check if all nodes lie on the fissure edge
10573         {
10574           bool notOnEdge = false;
10575           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10576             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10577           toAdd = notOnEdge;
10578         }
10579         if ( toAdd )
10580         {
10581           theAffectedElems.insert( eInv );
10582         }
10583       }
10584     }
10585
10586     return;
10587   } // findAffectedElems()
10588 } // namespace
10589
10590 //================================================================================
10591 /*!
10592  * \brief Create elements equal (on same nodes) to given ones
10593  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10594  *              elements of the uppest dimension are duplicated.
10595  */
10596 //================================================================================
10597
10598 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10599 {
10600   ClearLastCreated();
10601   SMESHDS_Mesh* mesh = GetMeshDS();
10602
10603   // get an element type and an iterator over elements
10604
10605   SMDSAbs_ElementType type = SMDSAbs_All;
10606   SMDS_ElemIteratorPtr elemIt;
10607   if ( theElements.empty() )
10608   {
10609     if ( mesh->NbNodes() == 0 )
10610       return;
10611     // get most complex type
10612     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10613       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10614       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10615     };
10616     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10617       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10618       {
10619         type = types[i];
10620         elemIt = mesh->elementsIterator( type );
10621         break;
10622       }
10623   }
10624   else
10625   {
10626     //type = (*theElements.begin())->GetType();
10627     elemIt = SMESHUtils::elemSetIterator( theElements );
10628   }
10629
10630   // un-mark all elements to avoid duplicating just created elements
10631   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10632
10633   // duplicate elements
10634
10635   ElemFeatures elemType;
10636
10637   vector< const SMDS_MeshNode* > nodes;
10638   while ( elemIt->more() )
10639   {
10640     const SMDS_MeshElement* elem = elemIt->next();
10641     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10642         ( elem->isMarked() ))
10643       continue;
10644
10645     elemType.Init( elem, /*basicOnly=*/false );
10646     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10647
10648     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10649       newElem->setIsMarked( true );
10650   }
10651 }
10652
10653 //================================================================================
10654 /*!
10655   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10656   \param theElems - the list of elements (edges or faces) to be replicated
10657   The nodes for duplication could be found from these elements
10658   \param theNodesNot - list of nodes to NOT replicate
10659   \param theAffectedElems - the list of elements (cells and edges) to which the
10660   replicated nodes should be associated to.
10661   \return TRUE if operation has been completed successfully, FALSE otherwise
10662 */
10663 //================================================================================
10664
10665 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10666                                     const TIDSortedElemSet& theNodesNot,
10667                                     const TIDSortedElemSet& theAffectedElems )
10668 {
10669   ClearLastCreated();
10670
10671   if ( theElems.size() == 0 )
10672     return false;
10673
10674   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10675   if ( !aMeshDS )
10676     return false;
10677
10678   bool res = false;
10679   TNodeNodeMap anOldNodeToNewNode;
10680   // duplicate elements and nodes
10681   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10682   // replce nodes by duplications
10683   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10684   return res;
10685 }
10686
10687 //================================================================================
10688 /*!
10689   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10690   \param theMeshDS - mesh instance
10691   \param theElems - the elements replicated or modified (nodes should be changed)
10692   \param theNodesNot - nodes to NOT replicate
10693   \param theNodeNodeMap - relation of old node to new created node
10694   \param theIsDoubleElem - flag os to replicate element or modify
10695   \return TRUE if operation has been completed successfully, FALSE otherwise
10696 */
10697 //================================================================================
10698
10699 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10700                                    const TIDSortedElemSet& theElems,
10701                                    const TIDSortedElemSet& theNodesNot,
10702                                    TNodeNodeMap&           theNodeNodeMap,
10703                                    const bool              theIsDoubleElem )
10704 {
10705   // iterate through element and duplicate them (by nodes duplication)
10706   bool res = false;
10707   std::vector<const SMDS_MeshNode*> newNodes;
10708   ElemFeatures elemType;
10709
10710   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10711   for ( ;  elemItr != theElems.end(); ++elemItr )
10712   {
10713     const SMDS_MeshElement* anElem = *elemItr;
10714     // if (!anElem)
10715     //   continue;
10716
10717     // duplicate nodes to duplicate element
10718     bool isDuplicate = false;
10719     newNodes.resize( anElem->NbNodes() );
10720     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10721     int ind = 0;
10722     while ( anIter->more() )
10723     {
10724       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10725       const SMDS_MeshNode*  aNewNode = aCurrNode;
10726       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10727       if ( n2n != theNodeNodeMap.end() )
10728       {
10729         aNewNode = n2n->second;
10730       }
10731       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10732       {
10733         // duplicate node
10734         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10735         copyPosition( aCurrNode, aNewNode );
10736         theNodeNodeMap[ aCurrNode ] = aNewNode;
10737         myLastCreatedNodes.push_back( aNewNode );
10738       }
10739       isDuplicate |= (aCurrNode != aNewNode);
10740       newNodes[ ind++ ] = aNewNode;
10741     }
10742     if ( !isDuplicate )
10743       continue;
10744
10745     if ( theIsDoubleElem )
10746       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10747     else
10748       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10749
10750     res = true;
10751   }
10752   return res;
10753 }
10754
10755 //================================================================================
10756 /*!
10757   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10758   \param theNodes - identifiers of nodes to be doubled
10759   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10760   nodes. If list of element identifiers is empty then nodes are doubled but
10761   they not assigned to elements
10762   \return TRUE if operation has been completed successfully, FALSE otherwise
10763 */
10764 //================================================================================
10765
10766 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10767                                     const std::list< int >& theListOfModifiedElems )
10768 {
10769   ClearLastCreated();
10770
10771   if ( theListOfNodes.size() == 0 )
10772     return false;
10773
10774   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10775   if ( !aMeshDS )
10776     return false;
10777
10778   // iterate through nodes and duplicate them
10779
10780   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10781
10782   std::list< int >::const_iterator aNodeIter;
10783   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10784   {
10785     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10786     if ( !aNode )
10787       continue;
10788
10789     // duplicate node
10790
10791     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10792     if ( aNewNode )
10793     {
10794       copyPosition( aNode, aNewNode );
10795       anOldNodeToNewNode[ aNode ] = aNewNode;
10796       myLastCreatedNodes.push_back( aNewNode );
10797     }
10798   }
10799
10800   // Change nodes of elements
10801
10802   std::vector<const SMDS_MeshNode*> aNodeArr;
10803
10804   std::list< int >::const_iterator anElemIter;
10805   for ( anElemIter =  theListOfModifiedElems.begin();
10806         anElemIter != theListOfModifiedElems.end();
10807         anElemIter++ )
10808   {
10809     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10810     if ( !anElem )
10811       continue;
10812
10813     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10814     for( size_t i = 0; i < aNodeArr.size(); ++i )
10815     {
10816       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10817         anOldNodeToNewNode.find( aNodeArr[ i ]);
10818       if ( n2n != anOldNodeToNewNode.end() )
10819         aNodeArr[ i ] = n2n->second;
10820     }
10821     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10822   }
10823
10824   return true;
10825 }
10826
10827 namespace {
10828
10829   //================================================================================
10830   /*!
10831     \brief Check if element located inside shape
10832     \return TRUE if IN or ON shape, FALSE otherwise
10833   */
10834   //================================================================================
10835
10836   template<class Classifier>
10837   bool isInside(const SMDS_MeshElement* theElem,
10838                 Classifier&             theClassifier,
10839                 const double            theTol)
10840   {
10841     gp_XYZ centerXYZ (0, 0, 0);
10842     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10843       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10844
10845     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10846     theClassifier.Perform(aPnt, theTol);
10847     TopAbs_State aState = theClassifier.State();
10848     return (aState == TopAbs_IN || aState == TopAbs_ON );
10849   }
10850
10851   //================================================================================
10852   /*!
10853    * \brief Classifier of the 3D point on the TopoDS_Face
10854    *        with interaface suitable for isInside()
10855    */
10856   //================================================================================
10857
10858   struct _FaceClassifier
10859   {
10860     Extrema_ExtPS       _extremum;
10861     BRepAdaptor_Surface _surface;
10862     TopAbs_State        _state;
10863
10864     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10865     {
10866       _extremum.Initialize( _surface,
10867                             _surface.FirstUParameter(), _surface.LastUParameter(),
10868                             _surface.FirstVParameter(), _surface.LastVParameter(),
10869                             _surface.Tolerance(), _surface.Tolerance() );
10870     }
10871     void Perform(const gp_Pnt& aPnt, double theTol)
10872     {
10873       theTol *= theTol;
10874       _state = TopAbs_OUT;
10875       _extremum.Perform(aPnt);
10876       if ( _extremum.IsDone() )
10877         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10878           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10879     }
10880     TopAbs_State State() const
10881     {
10882       return _state;
10883     }
10884   };
10885 }
10886
10887 //================================================================================
10888 /*!
10889   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10890   This method is the first step of DoubleNodeElemGroupsInRegion.
10891   \param theElems - list of groups of elements (edges or faces) to be replicated
10892   \param theNodesNot - list of groups of nodes not to replicated
10893   \param theShape - shape to detect affected elements (element which geometric center
10894          located on or inside shape). If the shape is null, detection is done on faces orientations
10895          (select elements with a gravity center on the side given by faces normals).
10896          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10897          The replicated nodes should be associated to affected elements.
10898   \return true
10899   \sa DoubleNodeElemGroupsInRegion()
10900 */
10901 //================================================================================
10902
10903 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10904                                                    const TIDSortedElemSet& theNodesNot,
10905                                                    const TopoDS_Shape&     theShape,
10906                                                    TIDSortedElemSet&       theAffectedElems)
10907 {
10908   if ( theShape.IsNull() )
10909   {
10910     findAffectedElems( theElems, theAffectedElems );
10911   }
10912   else
10913   {
10914     const double aTol = Precision::Confusion();
10915     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10916     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10917     if ( theShape.ShapeType() == TopAbs_SOLID )
10918     {
10919       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10920       bsc3d->PerformInfinitePoint(aTol);
10921     }
10922     else if (theShape.ShapeType() == TopAbs_FACE )
10923     {
10924       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10925     }
10926
10927     // iterates on indicated elements and get elements by back references from their nodes
10928     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10929     for ( ;  elemItr != theElems.end(); ++elemItr )
10930     {
10931       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10932       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10933       while ( nodeItr->more() )
10934       {
10935         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10936         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10937           continue;
10938         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10939         while ( backElemItr->more() )
10940         {
10941           const SMDS_MeshElement* curElem = backElemItr->next();
10942           if ( curElem && theElems.find(curElem) == theElems.end() &&
10943                ( bsc3d.get() ?
10944                  isInside( curElem, *bsc3d, aTol ) :
10945                  isInside( curElem, *aFaceClassifier, aTol )))
10946             theAffectedElems.insert( curElem );
10947         }
10948       }
10949     }
10950   }
10951   return true;
10952 }
10953
10954 //================================================================================
10955 /*!
10956   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10957   \param theElems - group of of elements (edges or faces) to be replicated
10958   \param theNodesNot - group of nodes not to replicate
10959   \param theShape - shape to detect affected elements (element which geometric center
10960   located on or inside shape).
10961   The replicated nodes should be associated to affected elements.
10962   \return TRUE if operation has been completed successfully, FALSE otherwise
10963 */
10964 //================================================================================
10965
10966 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10967                                             const TIDSortedElemSet& theNodesNot,
10968                                             const TopoDS_Shape&     theShape )
10969 {
10970   if ( theShape.IsNull() )
10971     return false;
10972
10973   const double aTol = Precision::Confusion();
10974   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10975   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10976   if ( theShape.ShapeType() == TopAbs_SOLID )
10977   {
10978     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10979     bsc3d->PerformInfinitePoint(aTol);
10980   }
10981   else if (theShape.ShapeType() == TopAbs_FACE )
10982   {
10983     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10984   }
10985
10986   // iterates on indicated elements and get elements by back references from their nodes
10987   TIDSortedElemSet anAffected;
10988   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10989   for ( ;  elemItr != theElems.end(); ++elemItr )
10990   {
10991     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10992     if (!anElem)
10993       continue;
10994
10995     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10996     while ( nodeItr->more() )
10997     {
10998       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10999       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11000         continue;
11001       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11002       while ( backElemItr->more() )
11003       {
11004         const SMDS_MeshElement* curElem = backElemItr->next();
11005         if ( curElem && theElems.find(curElem) == theElems.end() &&
11006              ( bsc3d ?
11007                isInside( curElem, *bsc3d, aTol ) :
11008                isInside( curElem, *aFaceClassifier, aTol )))
11009           anAffected.insert( curElem );
11010       }
11011     }
11012   }
11013   return DoubleNodes( theElems, theNodesNot, anAffected );
11014 }
11015
11016 /*!
11017  *  \brief compute an oriented angle between two planes defined by four points.
11018  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11019  *  @param p0 base of the rotation axe
11020  *  @param p1 extremity of the rotation axe
11021  *  @param g1 belongs to the first plane
11022  *  @param g2 belongs to the second plane
11023  */
11024 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11025 {
11026   gp_Vec vref(p0, p1);
11027   gp_Vec v1(p0, g1);
11028   gp_Vec v2(p0, g2);
11029   gp_Vec n1 = vref.Crossed(v1);
11030   gp_Vec n2 = vref.Crossed(v2);
11031   try {
11032     return n2.AngleWithRef(n1, vref);
11033   }
11034   catch ( Standard_Failure ) {
11035   }
11036   return Max( v1.Magnitude(), v2.Magnitude() );
11037 }
11038
11039 /*!
11040  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11041  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11042  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11043  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11044  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11045  * 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.
11046  * 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.
11047  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11048  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11049  * \param theElems - list of groups of volumes, where a group of volume is a set of
11050  *        SMDS_MeshElements sorted by Id.
11051  * \param createJointElems - if TRUE, create the elements
11052  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11053  *        the boundary between \a theDomains and the rest mesh
11054  * \return TRUE if operation has been completed successfully, FALSE otherwise
11055  */
11056 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11057                                                      bool                                 createJointElems,
11058                                                      bool                                 onAllBoundaries)
11059 {
11060   // MESSAGE("----------------------------------------------");
11061   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11062   // MESSAGE("----------------------------------------------");
11063
11064   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11065   meshDS->BuildDownWardConnectivity(true);
11066   CHRONO(50);
11067   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11068
11069   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11070   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11071   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11072
11073   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11074   std::map<int,int>celldom; // cell vtkId --> domain
11075   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11076   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11077   faceDomains.clear();
11078   celldom.clear();
11079   cellDomains.clear();
11080   nodeDomains.clear();
11081   std::map<int,int> emptyMap;
11082   std::set<int> emptySet;
11083   emptyMap.clear();
11084
11085   //MESSAGE(".. Number of domains :"<<theElems.size());
11086
11087   TIDSortedElemSet theRestDomElems;
11088   const int iRestDom  = -1;
11089   const int idom0     = onAllBoundaries ? iRestDom : 0;
11090   const int nbDomains = theElems.size();
11091
11092   // Check if the domains do not share an element
11093   for (int idom = 0; idom < nbDomains-1; idom++)
11094   {
11095     //       MESSAGE("... Check of domain #" << idom);
11096     const TIDSortedElemSet& domain = theElems[idom];
11097     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11098     for (; elemItr != domain.end(); ++elemItr)
11099     {
11100       const SMDS_MeshElement* anElem = *elemItr;
11101       int idombisdeb = idom + 1 ;
11102       // check if the element belongs to a domain further in the list
11103       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11104       {
11105         const TIDSortedElemSet& domainbis = theElems[idombis];
11106         if ( domainbis.count( anElem ))
11107         {
11108           MESSAGE(".... Domain #" << idom);
11109           MESSAGE(".... Domain #" << idombis);
11110           throw SALOME_Exception("The domains are not disjoint.");
11111           return false ;
11112         }
11113       }
11114     }
11115   }
11116
11117   for (int idom = 0; idom < nbDomains; idom++)
11118   {
11119
11120     // --- build a map (face to duplicate --> volume to modify)
11121     //     with all the faces shared by 2 domains (group of elements)
11122     //     and corresponding volume of this domain, for each shared face.
11123     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11124
11125     //MESSAGE("... Neighbors of domain #" << idom);
11126     const TIDSortedElemSet& domain = theElems[idom];
11127     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11128     for (; elemItr != domain.end(); ++elemItr)
11129     {
11130       const SMDS_MeshElement* anElem = *elemItr;
11131       if (!anElem)
11132         continue;
11133       int vtkId = anElem->GetVtkID();
11134       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11135       int neighborsVtkIds[NBMAXNEIGHBORS];
11136       int downIds[NBMAXNEIGHBORS];
11137       unsigned char downTypes[NBMAXNEIGHBORS];
11138       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11139       for (int n = 0; n < nbNeighbors; n++)
11140       {
11141         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11142         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11143         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11144         {
11145           bool ok = false;
11146           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11147           {
11148             // MESSAGE("Domain " << idombis);
11149             const TIDSortedElemSet& domainbis = theElems[idombis];
11150             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11151           }
11152           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11153           {
11154             DownIdType face(downIds[n], downTypes[n]);
11155             if (!faceDomains[face].count(idom))
11156             {
11157               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11158               celldom[vtkId] = idom;
11159               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11160             }
11161             if ( !ok )
11162             {
11163               theRestDomElems.insert( elem );
11164               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11165               celldom[neighborsVtkIds[n]] = iRestDom;
11166             }
11167           }
11168         }
11169       }
11170     }
11171   }
11172
11173   //MESSAGE("Number of shared faces " << faceDomains.size());
11174   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11175
11176   // --- explore the shared faces domain by domain,
11177   //     explore the nodes of the face and see if they belong to a cell in the domain,
11178   //     which has only a node or an edge on the border (not a shared face)
11179
11180   for (int idomain = idom0; idomain < nbDomains; idomain++)
11181   {
11182     //MESSAGE("Domain " << idomain);
11183     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11184     itface = faceDomains.begin();
11185     for (; itface != faceDomains.end(); ++itface)
11186     {
11187       const std::map<int, int>& domvol = itface->second;
11188       if (!domvol.count(idomain))
11189         continue;
11190       DownIdType face = itface->first;
11191       //MESSAGE(" --- face " << face.cellId);
11192       std::set<int> oldNodes;
11193       oldNodes.clear();
11194       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11195       std::set<int>::iterator itn = oldNodes.begin();
11196       for (; itn != oldNodes.end(); ++itn)
11197       {
11198         int oldId = *itn;
11199         //MESSAGE("     node " << oldId);
11200         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11201         for (int i=0; i<l.ncells; i++)
11202         {
11203           int vtkId = l.cells[i];
11204           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11205           if (!domain.count(anElem))
11206             continue;
11207           int vtkType = grid->GetCellType(vtkId);
11208           int downId = grid->CellIdToDownId(vtkId);
11209           if (downId < 0)
11210           {
11211             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11212             continue; // not OK at this stage of the algorithm:
11213             //no cells created after BuildDownWardConnectivity
11214           }
11215           DownIdType aCell(downId, vtkType);
11216           cellDomains[aCell][idomain] = vtkId;
11217           celldom[vtkId] = idomain;
11218           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11219         }
11220       }
11221     }
11222   }
11223
11224   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11225   //     for each shared face, get the nodes
11226   //     for each node, for each domain of the face, create a clone of the node
11227
11228   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11229   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11230   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11231
11232   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11233   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11234   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11235
11236   //MESSAGE(".. Duplication of the nodes");
11237   for (int idomain = idom0; idomain < nbDomains; idomain++)
11238   {
11239     itface = faceDomains.begin();
11240     for (; itface != faceDomains.end(); ++itface)
11241     {
11242       const std::map<int, int>& domvol = itface->second;
11243       if (!domvol.count(idomain))
11244         continue;
11245       DownIdType face = itface->first;
11246       //MESSAGE(" --- face " << face.cellId);
11247       std::set<int> oldNodes;
11248       oldNodes.clear();
11249       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11250       std::set<int>::iterator itn = oldNodes.begin();
11251       for (; itn != oldNodes.end(); ++itn)
11252       {
11253         int oldId = *itn;
11254         if (nodeDomains[oldId].empty())
11255         {
11256           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11257           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11258         }
11259         std::map<int, int>::const_iterator itdom = domvol.begin();
11260         for (; itdom != domvol.end(); ++itdom)
11261         {
11262           int idom = itdom->first;
11263           //MESSAGE("         domain " << idom);
11264           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11265           {
11266             if (nodeDomains[oldId].size() >= 2) // a multiple node
11267             {
11268               vector<int> orderedDoms;
11269               //MESSAGE("multiple node " << oldId);
11270               if (mutipleNodes.count(oldId))
11271                 orderedDoms = mutipleNodes[oldId];
11272               else
11273               {
11274                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11275                 for (; it != nodeDomains[oldId].end(); ++it)
11276                   orderedDoms.push_back(it->first);
11277               }
11278               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11279               //stringstream txt;
11280               //for (int i=0; i<orderedDoms.size(); i++)
11281               //  txt << orderedDoms[i] << " ";
11282               //MESSAGE("orderedDoms " << txt.str());
11283               mutipleNodes[oldId] = orderedDoms;
11284             }
11285             double *coords = grid->GetPoint(oldId);
11286             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11287             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11288             int newId = newNode->GetVtkID();
11289             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11290             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11291           }
11292         }
11293       }
11294     }
11295   }
11296
11297   //MESSAGE(".. Creation of elements");
11298   for (int idomain = idom0; idomain < nbDomains; idomain++)
11299   {
11300     itface = faceDomains.begin();
11301     for (; itface != faceDomains.end(); ++itface)
11302     {
11303       std::map<int, int> domvol = itface->second;
11304       if (!domvol.count(idomain))
11305         continue;
11306       DownIdType face = itface->first;
11307       //MESSAGE(" --- face " << face.cellId);
11308       std::set<int> oldNodes;
11309       oldNodes.clear();
11310       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11311       int nbMultipleNodes = 0;
11312       std::set<int>::iterator itn = oldNodes.begin();
11313       for (; itn != oldNodes.end(); ++itn)
11314       {
11315         int oldId = *itn;
11316         if (mutipleNodes.count(oldId))
11317           nbMultipleNodes++;
11318       }
11319       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11320       {
11321         //MESSAGE("multiple Nodes detected on a shared face");
11322         int downId = itface->first.cellId;
11323         unsigned char cellType = itface->first.cellType;
11324         // --- shared edge or shared face ?
11325         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11326         {
11327           int nodes[3];
11328           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11329           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11330             if (mutipleNodes.count(nodes[i]))
11331               if (!mutipleNodesToFace.count(nodes[i]))
11332                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11333         }
11334         else // shared face (between two volumes)
11335         {
11336           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11337           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11338           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11339           for (int ie =0; ie < nbEdges; ie++)
11340           {
11341             int nodes[3];
11342             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11343             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11344             {
11345               vector<int> vn0 = mutipleNodes[nodes[0]];
11346               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11347               vector<int> doms;
11348               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11349                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11350                   if ( vn0[i0] == vn1[i1] )
11351                     doms.push_back( vn0[ i0 ]);
11352               if ( doms.size() > 2 )
11353               {
11354                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11355                 double *coords = grid->GetPoint(nodes[0]);
11356                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11357                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11358                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11359                 gp_Pnt gref;
11360                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11361                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11362                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11363                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11364                 for ( size_t id = 0; id < doms.size(); id++ )
11365                 {
11366                   int idom = doms[id];
11367                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11368                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11369                   {
11370                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11371                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11372                     if (domain.count(elem))
11373                     {
11374                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11375                       domvol[idom] = (SMDS_MeshVolume*) svol;
11376                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11377                       double values[3] = { 0,0,0 };
11378                       vtkIdType npts = 0;
11379                       vtkIdType* pts = 0;
11380                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11381                       for ( vtkIdType i = 0; i < npts; ++i )
11382                       {
11383                         double *coords = grid->GetPoint( pts[i] );
11384                         for ( int j = 0; j < 3; ++j )
11385                           values[j] += coords[j] / npts;
11386                       }
11387                       if ( id == 0 )
11388                       {
11389                         gref.SetCoord( values[0], values[1], values[2] );
11390                         angleDom[idom] = 0;
11391                       }
11392                       else
11393                       {
11394                         gp_Pnt g( values[0], values[1], values[2] );
11395                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11396                         //MESSAGE("  angle=" << angleDom[idom]);
11397                       }
11398                       break;
11399                     }
11400                   }
11401                 }
11402                 map<double, int> sortedDom; // sort domains by angle
11403                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11404                   sortedDom[ia->second] = ia->first;
11405                 vector<int> vnodes;
11406                 vector<int> vdom;
11407                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11408                 {
11409                   vdom.push_back(ib->second);
11410                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11411                 }
11412                 for (int ino = 0; ino < nbNodes; ino++)
11413                   vnodes.push_back(nodes[ino]);
11414                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11415               }
11416             }
11417           }
11418         }
11419       }
11420     }
11421   }
11422
11423   // --- iterate on shared faces (volumes to modify, face to extrude)
11424   //     get node id's of the face (id SMDS = id VTK)
11425   //     create flat element with old and new nodes if requested
11426
11427   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11428   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11429
11430   std::map<int, std::map<long,int> > nodeQuadDomains;
11431   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11432
11433   //MESSAGE(".. Creation of elements: simple junction");
11434   if (createJointElems)
11435   {
11436     string joints2DName = "joints2D";
11437     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11438     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11439     string joints3DName = "joints3D";
11440     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11441     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11442
11443     itface = faceDomains.begin();
11444     for (; itface != faceDomains.end(); ++itface)
11445     {
11446       DownIdType face = itface->first;
11447       std::set<int> oldNodes;
11448       std::set<int>::iterator itn;
11449       oldNodes.clear();
11450       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11451
11452       std::map<int, int> domvol = itface->second;
11453       std::map<int, int>::iterator itdom = domvol.begin();
11454       int dom1 = itdom->first;
11455       int vtkVolId = itdom->second;
11456       itdom++;
11457       int dom2 = itdom->first;
11458       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11459                                                        nodeQuadDomains);
11460       stringstream grpname;
11461       grpname << "j_";
11462       if (dom1 < dom2)
11463         grpname << dom1 << "_" << dom2;
11464       else
11465         grpname << dom2 << "_" << dom1;
11466       string namegrp = grpname.str();
11467       if (!mapOfJunctionGroups.count(namegrp))
11468         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11469       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11470       if (sgrp)
11471         sgrp->Add(vol->GetID());
11472       if (vol->GetType() == SMDSAbs_Volume)
11473         joints3DGrp->Add(vol->GetID());
11474       else if (vol->GetType() == SMDSAbs_Face)
11475         joints2DGrp->Add(vol->GetID());
11476     }
11477   }
11478
11479   // --- create volumes on multiple domain intersection if requested
11480   //     iterate on mutipleNodesToFace
11481   //     iterate on edgesMultiDomains
11482
11483   //MESSAGE(".. Creation of elements: multiple junction");
11484   if (createJointElems)
11485   {
11486     // --- iterate on mutipleNodesToFace
11487
11488     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11489     for (; itn != mutipleNodesToFace.end(); ++itn)
11490     {
11491       int node = itn->first;
11492       vector<int> orderDom = itn->second;
11493       vector<vtkIdType> orderedNodes;
11494       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11495         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11496       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11497
11498       stringstream grpname;
11499       grpname << "m2j_";
11500       grpname << 0 << "_" << 0;
11501       string namegrp = grpname.str();
11502       if (!mapOfJunctionGroups.count(namegrp))
11503         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11504       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11505       if (sgrp)
11506         sgrp->Add(face->GetID());
11507     }
11508
11509     // --- iterate on edgesMultiDomains
11510
11511     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11512     for (; ite != edgesMultiDomains.end(); ++ite)
11513     {
11514       vector<int> nodes = ite->first;
11515       vector<int> orderDom = ite->second;
11516       vector<vtkIdType> orderedNodes;
11517       if (nodes.size() == 2)
11518       {
11519         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11520         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11521           if ( orderDom.size() == 3 )
11522             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11523               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11524           else
11525             for (int idom = orderDom.size()-1; idom >=0; idom--)
11526               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11527         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11528
11529         string namegrp = "jointsMultiples";
11530         if (!mapOfJunctionGroups.count(namegrp))
11531           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11532         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11533         if (sgrp)
11534           sgrp->Add(vol->GetID());
11535       }
11536       else
11537       {
11538         //INFOS("Quadratic multiple joints not implemented");
11539         // TODO quadratic nodes
11540       }
11541     }
11542   }
11543
11544   // --- list the explicit faces and edges of the mesh that need to be modified,
11545   //     i.e. faces and edges built with one or more duplicated nodes.
11546   //     associate these faces or edges to their corresponding domain.
11547   //     only the first domain found is kept when a face or edge is shared
11548
11549   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11550   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11551   faceOrEdgeDom.clear();
11552   feDom.clear();
11553
11554   //MESSAGE(".. Modification of elements");
11555   for (int idomain = idom0; idomain < nbDomains; idomain++)
11556   {
11557     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11558     for (; itnod != nodeDomains.end(); ++itnod)
11559     {
11560       int oldId = itnod->first;
11561       //MESSAGE("     node " << oldId);
11562       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11563       for (int i = 0; i < l.ncells; i++)
11564       {
11565         int vtkId = l.cells[i];
11566         int vtkType = grid->GetCellType(vtkId);
11567         int downId = grid->CellIdToDownId(vtkId);
11568         if (downId < 0)
11569           continue; // new cells: not to be modified
11570         DownIdType aCell(downId, vtkType);
11571         int volParents[1000];
11572         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11573         for (int j = 0; j < nbvol; j++)
11574           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11575             if (!feDom.count(vtkId))
11576             {
11577               feDom[vtkId] = idomain;
11578               faceOrEdgeDom[aCell] = emptyMap;
11579               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11580               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11581               //        << " type " << vtkType << " downId " << downId);
11582             }
11583       }
11584     }
11585   }
11586
11587   // --- iterate on shared faces (volumes to modify, face to extrude)
11588   //     get node id's of the face
11589   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11590
11591   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11592   for (int m=0; m<3; m++)
11593   {
11594     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11595     itface = (*amap).begin();
11596     for (; itface != (*amap).end(); ++itface)
11597     {
11598       DownIdType face = itface->first;
11599       std::set<int> oldNodes;
11600       std::set<int>::iterator itn;
11601       oldNodes.clear();
11602       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11603       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11604       std::map<int, int> localClonedNodeIds;
11605
11606       std::map<int, int> domvol = itface->second;
11607       std::map<int, int>::iterator itdom = domvol.begin();
11608       for (; itdom != domvol.end(); ++itdom)
11609       {
11610         int idom = itdom->first;
11611         int vtkVolId = itdom->second;
11612         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11613         localClonedNodeIds.clear();
11614         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11615         {
11616           int oldId = *itn;
11617           if (nodeDomains[oldId].count(idom))
11618           {
11619             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11620             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11621           }
11622         }
11623         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11624       }
11625     }
11626   }
11627
11628   // Remove empty groups (issue 0022812)
11629   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11630   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11631   {
11632     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11633       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11634   }
11635
11636   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11637   grid->DeleteLinks();
11638
11639   CHRONOSTOP(50);
11640   counters::stats();
11641   return true;
11642 }
11643
11644 /*!
11645  * \brief Double nodes on some external faces and create flat elements.
11646  * Flat elements are mainly used by some types of mechanic calculations.
11647  *
11648  * Each group of the list must be constituted of faces.
11649  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11650  * @param theElems - list of groups of faces, where a group of faces is a set of
11651  * SMDS_MeshElements sorted by Id.
11652  * @return TRUE if operation has been completed successfully, FALSE otherwise
11653  */
11654 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11655 {
11656   // MESSAGE("-------------------------------------------------");
11657   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11658   // MESSAGE("-------------------------------------------------");
11659
11660   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11661
11662   // --- For each group of faces
11663   //     duplicate the nodes, create a flat element based on the face
11664   //     replace the nodes of the faces by their clones
11665
11666   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11667   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11668   clonedNodes.clear();
11669   intermediateNodes.clear();
11670   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11671   mapOfJunctionGroups.clear();
11672
11673   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11674   {
11675     const TIDSortedElemSet&           domain = theElems[idom];
11676     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11677     for ( ; elemItr != domain.end(); ++elemItr )
11678     {
11679       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11680       if (!aFace)
11681         continue;
11682       // MESSAGE("aFace=" << aFace->GetID());
11683       bool isQuad = aFace->IsQuadratic();
11684       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11685
11686       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11687
11688       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11689       while (nodeIt->more())
11690       {
11691         const SMDS_MeshNode* node = nodeIt->next();
11692         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11693         if (isMedium)
11694           ln2.push_back(node);
11695         else
11696           ln0.push_back(node);
11697
11698         const SMDS_MeshNode* clone = 0;
11699         if (!clonedNodes.count(node))
11700         {
11701           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11702           copyPosition( node, clone );
11703           clonedNodes[node] = clone;
11704         }
11705         else
11706           clone = clonedNodes[node];
11707
11708         if (isMedium)
11709           ln3.push_back(clone);
11710         else
11711           ln1.push_back(clone);
11712
11713         const SMDS_MeshNode* inter = 0;
11714         if (isQuad && (!isMedium))
11715         {
11716           if (!intermediateNodes.count(node))
11717           {
11718             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11719             copyPosition( node, inter );
11720             intermediateNodes[node] = inter;
11721           }
11722           else
11723             inter = intermediateNodes[node];
11724           ln4.push_back(inter);
11725         }
11726       }
11727
11728       // --- extrude the face
11729
11730       vector<const SMDS_MeshNode*> ln;
11731       SMDS_MeshVolume* vol = 0;
11732       vtkIdType aType = aFace->GetVtkType();
11733       switch (aType)
11734       {
11735       case VTK_TRIANGLE:
11736         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11737         // MESSAGE("vol prism " << vol->GetID());
11738         ln.push_back(ln1[0]);
11739         ln.push_back(ln1[1]);
11740         ln.push_back(ln1[2]);
11741         break;
11742       case VTK_QUAD:
11743         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11744         // MESSAGE("vol hexa " << vol->GetID());
11745         ln.push_back(ln1[0]);
11746         ln.push_back(ln1[1]);
11747         ln.push_back(ln1[2]);
11748         ln.push_back(ln1[3]);
11749         break;
11750       case VTK_QUADRATIC_TRIANGLE:
11751         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11752                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11753         // MESSAGE("vol quad prism " << vol->GetID());
11754         ln.push_back(ln1[0]);
11755         ln.push_back(ln1[1]);
11756         ln.push_back(ln1[2]);
11757         ln.push_back(ln3[0]);
11758         ln.push_back(ln3[1]);
11759         ln.push_back(ln3[2]);
11760         break;
11761       case VTK_QUADRATIC_QUAD:
11762         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11763         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11764         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11765         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11766                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11767                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11768         // MESSAGE("vol quad hexa " << vol->GetID());
11769         ln.push_back(ln1[0]);
11770         ln.push_back(ln1[1]);
11771         ln.push_back(ln1[2]);
11772         ln.push_back(ln1[3]);
11773         ln.push_back(ln3[0]);
11774         ln.push_back(ln3[1]);
11775         ln.push_back(ln3[2]);
11776         ln.push_back(ln3[3]);
11777         break;
11778       case VTK_POLYGON:
11779         break;
11780       default:
11781         break;
11782       }
11783
11784       if (vol)
11785       {
11786         stringstream grpname;
11787         grpname << "jf_";
11788         grpname << idom;
11789         string namegrp = grpname.str();
11790         if (!mapOfJunctionGroups.count(namegrp))
11791           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11792         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11793         if (sgrp)
11794           sgrp->Add(vol->GetID());
11795       }
11796
11797       // --- modify the face
11798
11799       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11800     }
11801   }
11802   return true;
11803 }
11804
11805 /*!
11806  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11807  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11808  *  groups of faces to remove inside the object, (idem edges).
11809  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11810  */
11811 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11812                                       const TopoDS_Shape&             theShape,
11813                                       SMESH_NodeSearcher*             theNodeSearcher,
11814                                       const char*                     groupName,
11815                                       std::vector<double>&            nodesCoords,
11816                                       std::vector<std::vector<int> >& listOfListOfNodes)
11817 {
11818   // MESSAGE("--------------------------------");
11819   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11820   // MESSAGE("--------------------------------");
11821
11822   // --- zone of volumes to remove is given :
11823   //     1 either by a geom shape (one or more vertices) and a radius,
11824   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11825   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11826   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11827   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11828   //     defined by it's name.
11829
11830   SMESHDS_GroupBase* groupDS = 0;
11831   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11832   while ( groupIt->more() )
11833   {
11834     groupDS = 0;
11835     SMESH_Group * group = groupIt->next();
11836     if ( !group ) continue;
11837     groupDS = group->GetGroupDS();
11838     if ( !groupDS || groupDS->IsEmpty() ) continue;
11839     std::string grpName = group->GetName();
11840     //MESSAGE("grpName=" << grpName);
11841     if (grpName == groupName)
11842       break;
11843     else
11844       groupDS = 0;
11845   }
11846
11847   bool isNodeGroup = false;
11848   bool isNodeCoords = false;
11849   if (groupDS)
11850   {
11851     if (groupDS->GetType() != SMDSAbs_Node)
11852       return;
11853     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11854   }
11855
11856   if (nodesCoords.size() > 0)
11857     isNodeCoords = true; // a list o nodes given by their coordinates
11858   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11859
11860   // --- define groups to build
11861
11862   // --- group of SMDS volumes
11863   string grpvName = groupName;
11864   grpvName += "_vol";
11865   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11866   if (!grp)
11867   {
11868     MESSAGE("group not created " << grpvName);
11869     return;
11870   }
11871   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11872
11873   // --- group of SMDS faces on the skin
11874   string grpsName = groupName;
11875   grpsName += "_skin";
11876   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11877   if (!grps)
11878   {
11879     MESSAGE("group not created " << grpsName);
11880     return;
11881   }
11882   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11883
11884   // --- group of SMDS faces internal (several shapes)
11885   string grpiName = groupName;
11886   grpiName += "_internalFaces";
11887   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11888   if (!grpi)
11889   {
11890     MESSAGE("group not created " << grpiName);
11891     return;
11892   }
11893   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11894
11895   // --- group of SMDS faces internal (several shapes)
11896   string grpeiName = groupName;
11897   grpeiName += "_internalEdges";
11898   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11899   if (!grpei)
11900   {
11901     MESSAGE("group not created " << grpeiName);
11902     return;
11903   }
11904   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11905
11906   // --- build downward connectivity
11907
11908   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11909   meshDS->BuildDownWardConnectivity(true);
11910   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11911
11912   // --- set of volumes detected inside
11913
11914   std::set<int> setOfInsideVol;
11915   std::set<int> setOfVolToCheck;
11916
11917   std::vector<gp_Pnt> gpnts;
11918   gpnts.clear();
11919
11920   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11921   {
11922     //MESSAGE("group of nodes provided");
11923     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11924     while ( elemIt->more() )
11925     {
11926       const SMDS_MeshElement* elem = elemIt->next();
11927       if (!elem)
11928         continue;
11929       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11930       if (!node)
11931         continue;
11932       SMDS_MeshElement* vol = 0;
11933       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11934       while (volItr->more())
11935       {
11936         vol = (SMDS_MeshElement*)volItr->next();
11937         setOfInsideVol.insert(vol->GetVtkID());
11938         sgrp->Add(vol->GetID());
11939       }
11940     }
11941   }
11942   else if (isNodeCoords)
11943   {
11944     //MESSAGE("list of nodes coordinates provided");
11945     size_t i = 0;
11946     int k = 0;
11947     while ( i < nodesCoords.size()-2 )
11948     {
11949       double x = nodesCoords[i++];
11950       double y = nodesCoords[i++];
11951       double z = nodesCoords[i++];
11952       gp_Pnt p = gp_Pnt(x, y ,z);
11953       gpnts.push_back(p);
11954       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11955       k++;
11956     }
11957   }
11958   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11959   {
11960     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11961     TopTools_IndexedMapOfShape vertexMap;
11962     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11963     gp_Pnt p = gp_Pnt(0,0,0);
11964     if (vertexMap.Extent() < 1)
11965       return;
11966
11967     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11968     {
11969       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11970       p = BRep_Tool::Pnt(vertex);
11971       gpnts.push_back(p);
11972       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11973     }
11974   }
11975
11976   if (gpnts.size() > 0)
11977   {
11978     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11979     //MESSAGE("startNode->nodeId " << nodeId);
11980
11981     double radius2 = radius*radius;
11982     //MESSAGE("radius2 " << radius2);
11983
11984     // --- volumes on start node
11985
11986     setOfVolToCheck.clear();
11987     SMDS_MeshElement* startVol = 0;
11988     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11989     while (volItr->more())
11990     {
11991       startVol = (SMDS_MeshElement*)volItr->next();
11992       setOfVolToCheck.insert(startVol->GetVtkID());
11993     }
11994     if (setOfVolToCheck.empty())
11995     {
11996       MESSAGE("No volumes found");
11997       return;
11998     }
11999
12000     // --- starting with central volumes then their neighbors, check if they are inside
12001     //     or outside the domain, until no more new neighbor volume is inside.
12002     //     Fill the group of inside volumes
12003
12004     std::map<int, double> mapOfNodeDistance2;
12005     mapOfNodeDistance2.clear();
12006     std::set<int> setOfOutsideVol;
12007     while (!setOfVolToCheck.empty())
12008     {
12009       std::set<int>::iterator it = setOfVolToCheck.begin();
12010       int vtkId = *it;
12011       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12012       bool volInside = false;
12013       vtkIdType npts = 0;
12014       vtkIdType* pts = 0;
12015       grid->GetCellPoints(vtkId, npts, pts);
12016       for (int i=0; i<npts; i++)
12017       {
12018         double distance2 = 0;
12019         if (mapOfNodeDistance2.count(pts[i]))
12020         {
12021           distance2 = mapOfNodeDistance2[pts[i]];
12022           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12023         }
12024         else
12025         {
12026           double *coords = grid->GetPoint(pts[i]);
12027           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12028           distance2 = 1.E40;
12029           for ( size_t j = 0; j < gpnts.size(); j++ )
12030           {
12031             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12032             if (d2 < distance2)
12033             {
12034               distance2 = d2;
12035               if (distance2 < radius2)
12036                 break;
12037             }
12038           }
12039           mapOfNodeDistance2[pts[i]] = distance2;
12040           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12041         }
12042         if (distance2 < radius2)
12043         {
12044           volInside = true; // one or more nodes inside the domain
12045           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12046           break;
12047         }
12048       }
12049       if (volInside)
12050       {
12051         setOfInsideVol.insert(vtkId);
12052         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053         int neighborsVtkIds[NBMAXNEIGHBORS];
12054         int downIds[NBMAXNEIGHBORS];
12055         unsigned char downTypes[NBMAXNEIGHBORS];
12056         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12057         for (int n = 0; n < nbNeighbors; n++)
12058           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12059             setOfVolToCheck.insert(neighborsVtkIds[n]);
12060       }
12061       else
12062       {
12063         setOfOutsideVol.insert(vtkId);
12064         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12065       }
12066       setOfVolToCheck.erase(vtkId);
12067     }
12068   }
12069
12070   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12071   //     If yes, add the volume to the inside set
12072
12073   bool addedInside = true;
12074   std::set<int> setOfVolToReCheck;
12075   while (addedInside)
12076   {
12077     //MESSAGE(" --------------------------- re check");
12078     addedInside = false;
12079     std::set<int>::iterator itv = setOfInsideVol.begin();
12080     for (; itv != setOfInsideVol.end(); ++itv)
12081     {
12082       int vtkId = *itv;
12083       int neighborsVtkIds[NBMAXNEIGHBORS];
12084       int downIds[NBMAXNEIGHBORS];
12085       unsigned char downTypes[NBMAXNEIGHBORS];
12086       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12087       for (int n = 0; n < nbNeighbors; n++)
12088         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12089           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12090     }
12091     setOfVolToCheck = setOfVolToReCheck;
12092     setOfVolToReCheck.clear();
12093     while  (!setOfVolToCheck.empty())
12094     {
12095       std::set<int>::iterator it = setOfVolToCheck.begin();
12096       int vtkId = *it;
12097       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12098       {
12099         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12100         int countInside = 0;
12101         int neighborsVtkIds[NBMAXNEIGHBORS];
12102         int downIds[NBMAXNEIGHBORS];
12103         unsigned char downTypes[NBMAXNEIGHBORS];
12104         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12105         for (int n = 0; n < nbNeighbors; n++)
12106           if (setOfInsideVol.count(neighborsVtkIds[n]))
12107             countInside++;
12108         //MESSAGE("countInside " << countInside);
12109         if (countInside > 1)
12110         {
12111           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12112           setOfInsideVol.insert(vtkId);
12113           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12114           addedInside = true;
12115         }
12116         else
12117           setOfVolToReCheck.insert(vtkId);
12118       }
12119       setOfVolToCheck.erase(vtkId);
12120     }
12121   }
12122
12123   // --- map of Downward faces at the boundary, inside the global volume
12124   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12125   //     fill group of SMDS faces inside the volume (when several volume shapes)
12126   //     fill group of SMDS faces on the skin of the global volume (if skin)
12127
12128   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12129   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12130   std::set<int>::iterator it = setOfInsideVol.begin();
12131   for (; it != setOfInsideVol.end(); ++it)
12132   {
12133     int vtkId = *it;
12134     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12135     int neighborsVtkIds[NBMAXNEIGHBORS];
12136     int downIds[NBMAXNEIGHBORS];
12137     unsigned char downTypes[NBMAXNEIGHBORS];
12138     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12139     for (int n = 0; n < nbNeighbors; n++)
12140     {
12141       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12142       if (neighborDim == 3)
12143       {
12144         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12145         {
12146           DownIdType face(downIds[n], downTypes[n]);
12147           boundaryFaces[face] = vtkId;
12148         }
12149         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12150         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12151         if (vtkFaceId >= 0)
12152         {
12153           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12154           // find also the smds edges on this face
12155           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12156           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12157           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12158           for (int i = 0; i < nbEdges; i++)
12159           {
12160             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12161             if (vtkEdgeId >= 0)
12162               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12163           }
12164         }
12165       }
12166       else if (neighborDim == 2) // skin of the volume
12167       {
12168         DownIdType face(downIds[n], downTypes[n]);
12169         skinFaces[face] = vtkId;
12170         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12171         if (vtkFaceId >= 0)
12172           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12173       }
12174     }
12175   }
12176
12177   // --- identify the edges constituting the wire of each subshape on the skin
12178   //     define polylines with the nodes of edges, equivalent to wires
12179   //     project polylines on subshapes, and partition, to get geom faces
12180
12181   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12182   std::set<int> emptySet;
12183   emptySet.clear();
12184   std::set<int> shapeIds;
12185
12186   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12187   while (itelem->more())
12188   {
12189     const SMDS_MeshElement *elem = itelem->next();
12190     int shapeId = elem->getshapeId();
12191     int   vtkId = elem->GetVtkID();
12192     if (!shapeIdToVtkIdSet.count(shapeId))
12193     {
12194       shapeIdToVtkIdSet[shapeId] = emptySet;
12195       shapeIds.insert(shapeId);
12196     }
12197     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12198   }
12199
12200   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12201   std::set<DownIdType, DownIdCompare> emptyEdges;
12202   emptyEdges.clear();
12203
12204   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12205   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12206   {
12207     int shapeId = itShape->first;
12208     //MESSAGE(" --- Shape ID --- "<< shapeId);
12209     shapeIdToEdges[shapeId] = emptyEdges;
12210
12211     std::vector<int> nodesEdges;
12212
12213     std::set<int>::iterator its = itShape->second.begin();
12214     for (; its != itShape->second.end(); ++its)
12215     {
12216       int vtkId = *its;
12217       //MESSAGE("     " << vtkId);
12218       int neighborsVtkIds[NBMAXNEIGHBORS];
12219       int downIds[NBMAXNEIGHBORS];
12220       unsigned char downTypes[NBMAXNEIGHBORS];
12221       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12222       for (int n = 0; n < nbNeighbors; n++)
12223       {
12224         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12225           continue;
12226         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12227         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12228         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12229         {
12230           DownIdType edge(downIds[n], downTypes[n]);
12231           if (!shapeIdToEdges[shapeId].count(edge))
12232           {
12233             shapeIdToEdges[shapeId].insert(edge);
12234             int vtkNodeId[3];
12235             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12236             nodesEdges.push_back(vtkNodeId[0]);
12237             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12238             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12239           }
12240         }
12241       }
12242     }
12243
12244     std::list<int> order;
12245     order.clear();
12246     if (nodesEdges.size() > 0)
12247     {
12248       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12249       nodesEdges[0] = -1;
12250       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12251       nodesEdges[1] = -1; // do not reuse this edge
12252       bool found = true;
12253       while (found)
12254       {
12255         int nodeTofind = order.back(); // try first to push back
12256         int i = 0;
12257         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12258           if (nodesEdges[i] == nodeTofind)
12259             break;
12260         if ( i == (int) nodesEdges.size() )
12261           found = false; // no follower found on back
12262         else
12263         {
12264           if (i%2) // odd ==> use the previous one
12265             if (nodesEdges[i-1] < 0)
12266               found = false;
12267             else
12268             {
12269               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12270               nodesEdges[i-1] = -1;
12271             }
12272           else // even ==> use the next one
12273             if (nodesEdges[i+1] < 0)
12274               found = false;
12275             else
12276             {
12277               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12278               nodesEdges[i+1] = -1;
12279             }
12280         }
12281         if (found)
12282           continue;
12283         // try to push front
12284         found = true;
12285         nodeTofind = order.front(); // try to push front
12286         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12287           if ( nodesEdges[i] == nodeTofind )
12288             break;
12289         if ( i == (int)nodesEdges.size() )
12290         {
12291           found = false; // no predecessor found on front
12292           continue;
12293         }
12294         if (i%2) // odd ==> use the previous one
12295           if (nodesEdges[i-1] < 0)
12296             found = false;
12297           else
12298           {
12299             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12300             nodesEdges[i-1] = -1;
12301           }
12302         else // even ==> use the next one
12303           if (nodesEdges[i+1] < 0)
12304             found = false;
12305           else
12306           {
12307             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12308             nodesEdges[i+1] = -1;
12309           }
12310       }
12311     }
12312
12313
12314     std::vector<int> nodes;
12315     nodes.push_back(shapeId);
12316     std::list<int>::iterator itl = order.begin();
12317     for (; itl != order.end(); itl++)
12318     {
12319       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12320       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12321     }
12322     listOfListOfNodes.push_back(nodes);
12323   }
12324
12325   //     partition geom faces with blocFissure
12326   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12327   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12328
12329   return;
12330 }
12331
12332
12333 //================================================================================
12334 /*!
12335  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12336  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12337  * \return TRUE if operation has been completed successfully, FALSE otherwise
12338  */
12339 //================================================================================
12340
12341 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12342 {
12343   // iterates on volume elements and detect all free faces on them
12344   SMESHDS_Mesh* aMesh = GetMeshDS();
12345   if (!aMesh)
12346     return false;
12347
12348   ElemFeatures faceType( SMDSAbs_Face );
12349   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12350   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12351   while(vIt->more())
12352   {
12353     const SMDS_MeshVolume* volume = vIt->next();
12354     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12355     vTool.SetExternalNormal();
12356     const int iQuad = volume->IsQuadratic();
12357     faceType.SetQuad( iQuad );
12358     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12359     {
12360       if (!vTool.IsFreeFace(iface))
12361         continue;
12362       nbFree++;
12363       vector<const SMDS_MeshNode *> nodes;
12364       int nbFaceNodes = vTool.NbFaceNodes(iface);
12365       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12366       int inode = 0;
12367       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12368         nodes.push_back(faceNodes[inode]);
12369
12370       if (iQuad) // add medium nodes
12371       {
12372         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12373           nodes.push_back(faceNodes[inode]);
12374         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12375           nodes.push_back(faceNodes[8]);
12376       }
12377       // add new face based on volume nodes
12378       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12379       {
12380         nbExisted++; // face already exists
12381       }
12382       else
12383       {
12384         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12385         nbCreated++;
12386       }
12387     }
12388   }
12389   return ( nbFree == ( nbExisted + nbCreated ));
12390 }
12391
12392 namespace
12393 {
12394   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12395   {
12396     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12397       return n;
12398     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12399   }
12400 }
12401 //================================================================================
12402 /*!
12403  * \brief Creates missing boundary elements
12404  *  \param elements - elements whose boundary is to be checked
12405  *  \param dimension - defines type of boundary elements to create
12406  *  \param group - a group to store created boundary elements in
12407  *  \param targetMesh - a mesh to store created boundary elements in
12408  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12409  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12410  *                                boundary elements will be copied into the targetMesh
12411  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12412  *                                boundary elements will be added into the new group
12413  *  \param aroundElements - if true, elements will be created on boundary of given
12414  *                          elements else, on boundary of the whole mesh.
12415  * \return nb of added boundary elements
12416  */
12417 //================================================================================
12418
12419 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12420                                        Bnd_Dimension           dimension,
12421                                        SMESH_Group*            group/*=0*/,
12422                                        SMESH_Mesh*             targetMesh/*=0*/,
12423                                        bool                    toCopyElements/*=false*/,
12424                                        bool                    toCopyExistingBoundary/*=false*/,
12425                                        bool                    toAddExistingBondary/*= false*/,
12426                                        bool                    aroundElements/*= false*/)
12427 {
12428   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12429   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12430   // hope that all elements are of the same type, do not check them all
12431   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12432     throw SALOME_Exception(LOCALIZED("wrong element type"));
12433
12434   if ( !targetMesh )
12435     toCopyElements = toCopyExistingBoundary = false;
12436
12437   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12438   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12439   int nbAddedBnd = 0;
12440
12441   // editor adding present bnd elements and optionally holding elements to add to the group
12442   SMESH_MeshEditor* presentEditor;
12443   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12444   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12445
12446   SMESH_MesherHelper helper( *myMesh );
12447   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12448   SMDS_VolumeTool vTool;
12449   TIDSortedElemSet avoidSet;
12450   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12451   size_t inode;
12452
12453   typedef vector<const SMDS_MeshNode*> TConnectivity;
12454   TConnectivity tgtNodes;
12455   ElemFeatures elemKind( missType ), elemToCopy;
12456
12457   vector<const SMDS_MeshElement*> presentBndElems;
12458   vector<TConnectivity>           missingBndElems;
12459   vector<int>                     freeFacets;
12460   TConnectivity nodes, elemNodes;
12461
12462   SMDS_ElemIteratorPtr eIt;
12463   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12464   else                  eIt = SMESHUtils::elemSetIterator( elements );
12465
12466   while ( eIt->more() )
12467   {
12468     const SMDS_MeshElement* elem = eIt->next();
12469     const int              iQuad = elem->IsQuadratic();
12470     elemKind.SetQuad( iQuad );
12471
12472     // ------------------------------------------------------------------------------------
12473     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12474     // ------------------------------------------------------------------------------------
12475     presentBndElems.clear();
12476     missingBndElems.clear();
12477     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12478     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12479     {
12480       const SMDS_MeshElement* otherVol = 0;
12481       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12482       {
12483         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12484              ( !aroundElements || elements.count( otherVol )))
12485           continue;
12486         freeFacets.push_back( iface );
12487       }
12488       if ( missType == SMDSAbs_Face )
12489         vTool.SetExternalNormal();
12490       for ( size_t i = 0; i < freeFacets.size(); ++i )
12491       {
12492         int                iface = freeFacets[i];
12493         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12494         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12495         if ( missType == SMDSAbs_Edge ) // boundary edges
12496         {
12497           nodes.resize( 2+iQuad );
12498           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12499           {
12500             for ( size_t j = 0; j < nodes.size(); ++j )
12501               nodes[ j ] = nn[ i+j ];
12502             if ( const SMDS_MeshElement* edge =
12503                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12504               presentBndElems.push_back( edge );
12505             else
12506               missingBndElems.push_back( nodes );
12507           }
12508         }
12509         else // boundary face
12510         {
12511           nodes.clear();
12512           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12513             nodes.push_back( nn[inode] ); // add corner nodes
12514           if (iQuad)
12515             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12516               nodes.push_back( nn[inode] ); // add medium nodes
12517           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12518           if ( iCenter > 0 )
12519             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12520
12521           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12522                                                                SMDSAbs_Face, /*noMedium=*/false ))
12523             presentBndElems.push_back( f );
12524           else
12525             missingBndElems.push_back( nodes );
12526
12527           if ( targetMesh != myMesh )
12528           {
12529             // add 1D elements on face boundary to be added to a new mesh
12530             const SMDS_MeshElement* edge;
12531             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12532             {
12533               if ( iQuad )
12534                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12535               else
12536                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12537               if ( edge && avoidSet.insert( edge ).second )
12538                 presentBndElems.push_back( edge );
12539             }
12540           }
12541         }
12542       }
12543     }
12544     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12545     {
12546       avoidSet.clear(), avoidSet.insert( elem );
12547       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12548                         SMDS_MeshElement::iterator() );
12549       elemNodes.push_back( elemNodes[0] );
12550       nodes.resize( 2 + iQuad );
12551       const int nbLinks = elem->NbCornerNodes();
12552       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12553       {
12554         nodes[0] = elemNodes[iN];
12555         nodes[1] = elemNodes[iN+1+iQuad];
12556         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12557           continue; // not free link
12558
12559         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12560         if ( const SMDS_MeshElement* edge =
12561              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12562           presentBndElems.push_back( edge );
12563         else
12564           missingBndElems.push_back( nodes );
12565       }
12566     }
12567
12568     // ---------------------------------
12569     // 2. Add missing boundary elements
12570     // ---------------------------------
12571     if ( targetMesh != myMesh )
12572       // instead of making a map of nodes in this mesh and targetMesh,
12573       // we create nodes with same IDs.
12574       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12575       {
12576         TConnectivity& srcNodes = missingBndElems[i];
12577         tgtNodes.resize( srcNodes.size() );
12578         for ( inode = 0; inode < srcNodes.size(); ++inode )
12579           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12580         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12581                                                                        missType,
12582                                                                        /*noMedium=*/false))
12583           continue;
12584         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12585         ++nbAddedBnd;
12586       }
12587     else
12588       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12589       {
12590         TConnectivity& nodes = missingBndElems[ i ];
12591         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12592                                                                        missType,
12593                                                                        /*noMedium=*/false))
12594           continue;
12595         SMDS_MeshElement* newElem =
12596           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12597         nbAddedBnd += bool( newElem );
12598
12599         // try to set a new element to a shape
12600         if ( myMesh->HasShapeToMesh() )
12601         {
12602           bool ok = true;
12603           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12604           const size_t nbN = nodes.size() / (iQuad+1 );
12605           for ( inode = 0; inode < nbN && ok; ++inode )
12606           {
12607             pair<int, TopAbs_ShapeEnum> i_stype =
12608               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12609             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12610               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12611           }
12612           if ( ok && mediumShapes.size() > 1 )
12613           {
12614             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12615             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12616             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12617             {
12618               if (( ok = ( stype_i->first != stype_i_0.first )))
12619                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12620                                         aMesh->IndexToShape( stype_i_0.second ));
12621             }
12622           }
12623           if ( ok && mediumShapes.begin()->first == missShapeType )
12624             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12625         }
12626       }
12627
12628     // ----------------------------------
12629     // 3. Copy present boundary elements
12630     // ----------------------------------
12631     if ( toCopyExistingBoundary )
12632       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12633       {
12634         const SMDS_MeshElement* e = presentBndElems[i];
12635         tgtNodes.resize( e->NbNodes() );
12636         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12637           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12638         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12639       }
12640     else // store present elements to add them to a group
12641       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12642       {
12643         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12644       }
12645
12646   } // loop on given elements
12647
12648   // ---------------------------------------------
12649   // 4. Fill group with boundary elements
12650   // ---------------------------------------------
12651   if ( group )
12652   {
12653     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12654       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12655         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12656   }
12657   tgtEditor.myLastCreatedElems.clear();
12658   tgtEditor2.myLastCreatedElems.clear();
12659
12660   // -----------------------
12661   // 5. Copy given elements
12662   // -----------------------
12663   if ( toCopyElements && targetMesh != myMesh )
12664   {
12665     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12666     else                  eIt = SMESHUtils::elemSetIterator( elements );
12667     while (eIt->more())
12668     {
12669       const SMDS_MeshElement* elem = eIt->next();
12670       tgtNodes.resize( elem->NbNodes() );
12671       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12672         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12673       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12674
12675       tgtEditor.myLastCreatedElems.clear();
12676     }
12677   }
12678   return nbAddedBnd;
12679 }
12680
12681 //================================================================================
12682 /*!
12683  * \brief Copy node position and set \a to node on the same geometry
12684  */
12685 //================================================================================
12686
12687 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12688                                      const SMDS_MeshNode* to )
12689 {
12690   if ( !from || !to ) return;
12691
12692   SMDS_PositionPtr pos = from->GetPosition();
12693   if ( !pos || from->getshapeId() < 1 ) return;
12694
12695   switch ( pos->GetTypeOfPosition() )
12696   {
12697   case SMDS_TOP_3DSPACE: break;
12698
12699   case SMDS_TOP_FACE:
12700   {
12701     SMDS_FacePositionPtr fPos = pos;
12702     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12703                                 fPos->GetUParameter(), fPos->GetVParameter() );
12704     break;
12705   }
12706   case SMDS_TOP_EDGE:
12707   {
12708     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12709     SMDS_EdgePositionPtr ePos = pos;
12710     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12711     break;
12712   }
12713   case SMDS_TOP_VERTEX:
12714   {
12715     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12716     break;
12717   }
12718   case SMDS_TOP_UNSPEC:
12719   default:;
12720   }
12721 }