Salome HOME
introduce biquadratic quadratic pentahedron (18 nodes)
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Return mesh DS
131  */
132 //================================================================================
133
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
135 {
136   return myMesh->GetMeshDS();
137 }
138
139
140 //================================================================================
141 /*!
142  * \brief Clears myLastCreatedNodes and myLastCreatedElems
143  */
144 //================================================================================
145
146 void SMESH_MeshEditor::ClearLastCreated()
147 {
148   myLastCreatedNodes.Clear();
149   myLastCreatedElems.Clear();
150 }
151
152 //================================================================================
153 /*!
154  * \brief Initializes members by an existing element
155  *  \param [in] elem - the source element
156  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
157  */
158 //================================================================================
159
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
162 {
163   if ( elem )
164   {
165     myType = elem->GetType();
166     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
167     {
168       myIsPoly = elem->IsPoly();
169       if ( myIsPoly )
170       {
171         myIsQuad = elem->IsQuadratic();
172         if ( myType == SMDSAbs_Volume && !basicOnly )
173         {
174           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175           myPolyhedQuantities.swap( quant );
176         }
177       }
178     }
179     else if ( myType == SMDSAbs_Ball && !basicOnly )
180     {
181       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
182     }
183   }
184   return *this;
185 }
186
187 //=======================================================================
188 /*!
189  * \brief Add element
190  */
191 //=======================================================================
192
193 SMDS_MeshElement*
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195                              const ElemFeatures&                  features)
196 {
197   SMDS_MeshElement* e = 0;
198   int nbnode = node.size();
199   SMESHDS_Mesh* mesh = GetMeshDS();
200   const int ID = features.myID;
201
202   switch ( features.myType ) {
203   case SMDSAbs_Face:
204     if ( !features.myIsPoly ) {
205       if      (nbnode == 3) {
206         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2] );
208       }
209       else if (nbnode == 4) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
212       }
213       else if (nbnode == 6) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5] );
218       }
219       else if (nbnode == 7) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6] );
224       }
225       else if (nbnode == 8) {
226         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7], ID);
228         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7] );
230       }
231       else if (nbnode == 9) {
232         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233                                                node[4], node[5], node[6], node[7], node[8], ID);
234         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
235                                                node[4], node[5], node[6], node[7], node[8] );
236       }
237     }
238     else if ( !features.myIsQuad )
239     {
240       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241       else           e = mesh->AddPolygonalFace      (node    );
242     }
243     else if ( nbnode % 2 == 0 ) // just a protection
244     {
245       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246       else           e = mesh->AddQuadPolygonalFace      (node    );
247     }
248     break;
249
250   case SMDSAbs_Volume:
251     if ( !features.myIsPoly ) {
252       if      (nbnode == 4) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
255       }
256       else if (nbnode == 5) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4] );
261       }
262       else if (nbnode == 6) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5] );
267       }
268       else if (nbnode == 8) {
269         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7], ID);
271         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7] );
273       }
274       else if (nbnode == 10) {
275         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9] );
281       }
282       else if (nbnode == 12) {
283         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10], node[11], ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10], node[11] );
289       }
290       else if (nbnode == 13) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12] );
299       }
300       else if (nbnode == 15) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],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       }
310       else if (nbnode == 20) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],ID);
316         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
317                                                  node[4], node[5], node[6], node[7],
318                                                  node[8], node[9], node[10],node[11],
319                                                  node[12],node[13],node[14],node[15],
320                                                  node[16],node[17],node[18],node[19] );
321       }
322       else if (nbnode == 27) {
323         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324                                                  node[4], node[5], node[6], node[7],
325                                                  node[8], node[9], node[10],node[11],
326                                                  node[12],node[13],node[14],node[15],
327                                                  node[16],node[17],node[18],node[19],
328                                                  node[20],node[21],node[22],node[23],
329                                                  node[24],node[25],node[26], ID);
330         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
331                                                  node[4], node[5], node[6], node[7],
332                                                  node[8], node[9], node[10],node[11],
333                                                  node[12],node[13],node[14],node[15],
334                                                  node[16],node[17],node[18],node[19],
335                                                  node[20],node[21],node[22],node[23],
336                                                  node[24],node[25],node[26] );
337       }
338     }
339     else if ( !features.myIsQuad )
340     {
341       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
343     }
344     else
345     {
346       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
348     }
349     break;
350
351   case SMDSAbs_Edge:
352     if ( nbnode == 2 ) {
353       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354       else           e = mesh->AddEdge      (node[0], node[1] );
355     }
356     else if ( nbnode == 3 ) {
357       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
359     }
360     break;
361
362   case SMDSAbs_0DElement:
363     if ( nbnode == 1 ) {
364       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365       else           e = mesh->Add0DElement      (node[0] );
366     }
367     break;
368
369   case SMDSAbs_Node:
370     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
372     break;
373
374   case SMDSAbs_Ball:
375     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
377     break;
378
379   default:;
380   }
381   if ( e ) myLastCreatedElems.Append( e );
382   return e;
383 }
384
385 //=======================================================================
386 /*!
387  * \brief Add element
388  */
389 //=======================================================================
390
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392                                                const ElemFeatures& features)
393 {
394   vector<const SMDS_MeshNode*> nodes;
395   nodes.reserve( nodeIDs.size() );
396   vector<int>::const_iterator id = nodeIDs.begin();
397   while ( id != nodeIDs.end() ) {
398     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399       nodes.push_back( node );
400     else
401       return 0;
402   }
403   return AddElement( nodes, features );
404 }
405
406 //=======================================================================
407 //function : Remove
408 //purpose  : Remove a node or an element.
409 //           Modify a compute state of sub-meshes which become empty
410 //=======================================================================
411
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
413                               const bool         isNodes )
414 {
415   myLastCreatedElems.Clear();
416   myLastCreatedNodes.Clear();
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 = 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.Append( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.Last() );
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   myLastCreatedElems.Clear();
527   myLastCreatedNodes.Clear();
528
529   SMESHDS_Mesh * aMesh = GetMeshDS();
530   if ( aMesh->ShapeToMesh().IsNull() )
531     return 0;
532
533   int aShapeID = theElem->getshapeId();
534   if ( aShapeID < 1 )
535     return 0;
536
537   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538     if ( sm->Contains( theElem ))
539       return aShapeID;
540
541   if ( theElem->GetType() == SMDSAbs_Node ) {
542     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
543   }
544   else {
545     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
546   }
547
548   TopoDS_Shape aShape; // the shape a node of theElem is on
549   if ( theElem->GetType() != SMDSAbs_Node )
550   {
551     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552     while ( nodeIt->more() ) {
553       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554       if ((aShapeID = node->getshapeId()) > 0) {
555         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556           if ( sm->Contains( theElem ))
557             return aShapeID;
558           if ( aShape.IsNull() )
559             aShape = aMesh->IndexToShape( aShapeID );
560         }
561       }
562     }
563   }
564
565   // None of nodes is on a proper shape,
566   // find the shape among ancestors of aShape on which a node is
567   if ( !aShape.IsNull() ) {
568     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569     for ( ; ancIt.More(); ancIt.Next() ) {
570       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571       if ( sm && sm->Contains( theElem ))
572         return aMesh->ShapeToIndex( ancIt.Value() );
573     }
574   }
575   else
576   {
577     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578     while ( const SMESHDS_SubMesh* sm = smIt->next() )
579       if ( sm->Contains( theElem ))
580         return sm->GetID();
581   }
582
583   return 0;
584 }
585
586 //=======================================================================
587 //function : IsMedium
588 //purpose  :
589 //=======================================================================
590
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
592                                 const SMDSAbs_ElementType typeToCheck)
593 {
594   bool isMedium = false;
595   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596   while (it->more() && !isMedium ) {
597     const SMDS_MeshElement* elem = it->next();
598     isMedium = elem->IsMediumNode(node);
599   }
600   return isMedium;
601 }
602
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose  : Shift nodes in the array corresponded to quadratic triangle
606 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
608
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 {
611   const SMDS_MeshNode* nd1 = aNodes[0];
612   aNodes[0] = aNodes[1];
613   aNodes[1] = aNodes[2];
614   aNodes[2] = nd1;
615   const SMDS_MeshNode* nd2 = aNodes[3];
616   aNodes[3] = aNodes[4];
617   aNodes[4] = aNodes[5];
618   aNodes[5] = nd2;
619 }
620
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose  : return number of the edges connected with the theNode.
624 //           if theEdges has connections with the other type of the
625 //           elements, return -1
626 //=======================================================================
627
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
629 {
630   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
631   // int nb=0;
632   // while(elemIt->more()) {
633   //   elemIt->next();
634   //   nb++;
635   // }
636   // return nb;
637   return theNode->NbInverseElements();
638 }
639
640 //=======================================================================
641 //function : getNodesFromTwoTria
642 //purpose  : 
643 //=======================================================================
644
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646                                 const SMDS_MeshElement * theTria2,
647                                 vector< const SMDS_MeshNode*>& N1,
648                                 vector< const SMDS_MeshNode*>& N2)
649 {
650   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651   if ( N1.size() < 6 ) return false;
652   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653   if ( N2.size() < 6 ) return false;
654
655   int sames[3] = {-1,-1,-1};
656   int nbsames = 0;
657   int i, j;
658   for(i=0; i<3; i++) {
659     for(j=0; j<3; j++) {
660       if(N1[i]==N2[j]) {
661         sames[i] = j;
662         nbsames++;
663         break;
664       }
665     }
666   }
667   if(nbsames!=2) return false;
668   if(sames[0]>-1) {
669     shiftNodesQuadTria(N1);
670     if(sames[1]>-1) {
671       shiftNodesQuadTria(N1);
672     }
673   }
674   i = sames[0] + sames[1] + sames[2];
675   for(; i<2; i++) {
676     shiftNodesQuadTria(N2);
677   }
678   // now we receive following N1 and N2 (using numeration as in the image below)
679   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
680   // i.e. first nodes from both arrays form a new diagonal
681   return true;
682 }
683
684 //=======================================================================
685 //function : InverseDiag
686 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
687 //           but having other common link.
688 //           Return False if args are improper
689 //=======================================================================
690
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692                                     const SMDS_MeshElement * theTria2 )
693 {
694   myLastCreatedElems.Clear();
695   myLastCreatedNodes.Clear();
696
697   if (!theTria1 || !theTria2)
698     return false;
699
700   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701   if (!F1) return false;
702   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703   if (!F2) return false;
704   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
706
707     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
709     //    |/ |                                         | \|
710     //  B +--+ 2                                     B +--+ 2
711
712     // put nodes in array and find out indices of the same ones
713     const SMDS_MeshNode* aNodes [6];
714     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
715     int i = 0;
716     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717     while ( it->more() ) {
718       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
719
720       if ( i > 2 ) // theTria2
721         // find same node of theTria1
722         for ( int j = 0; j < 3; j++ )
723           if ( aNodes[ i ] == aNodes[ j ]) {
724             sameInd[ j ] = i;
725             sameInd[ i ] = j;
726             break;
727           }
728       // next
729       i++;
730       if ( i == 3 ) {
731         if ( it->more() )
732           return false; // theTria1 is not a triangle
733         it = theTria2->nodesIterator();
734       }
735       if ( i == 6 && it->more() )
736         return false; // theTria2 is not a triangle
737     }
738
739     // find indices of 1,2 and of A,B in theTria1
740     int iA = -1, iB = 0, i1 = 0, i2 = 0;
741     for ( i = 0; i < 6; i++ ) {
742       if ( sameInd [ i ] == -1 ) {
743         if ( i < 3 ) i1 = i;
744         else         i2 = i;
745       }
746       else if (i < 3) {
747         if ( iA >= 0) iB = i;
748         else          iA = i;
749       }
750     }
751     // nodes 1 and 2 should not be the same
752     if ( aNodes[ i1 ] == aNodes[ i2 ] )
753       return false;
754
755     // theTria1: A->2
756     aNodes[ iA ] = aNodes[ i2 ];
757     // theTria2: B->1
758     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
759
760     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
762
763     return true;
764
765   } // end if(F1 && F2)
766
767   // check case of quadratic faces
768   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
770     return false;
771   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773     return false;
774
775   //       5
776   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778   //    |   / |
779   //  7 +  +  + 6
780   //    | /9  |
781   //    |/    |
782   //  4 +--+--+ 3
783   //       8
784
785   vector< const SMDS_MeshNode* > N1;
786   vector< const SMDS_MeshNode* > N2;
787   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
788     return false;
789   // now we receive following N1 and N2 (using numeration as above image)
790   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
791   // i.e. first nodes from both arrays determ new diagonal
792
793   vector< const SMDS_MeshNode*> N1new( N1.size() );
794   vector< const SMDS_MeshNode*> N2new( N2.size() );
795   N1new.back() = N1.back(); // central node of biquadratic
796   N2new.back() = N2.back();
797   N1new[0] = N1[0];  N2new[0] = N1[0];
798   N1new[1] = N2[0];  N2new[1] = N1[1];
799   N1new[2] = N2[1];  N2new[2] = N2[0];
800   N1new[3] = N1[4];  N2new[3] = N1[3];
801   N1new[4] = N2[3];  N2new[4] = N2[5];
802   N1new[5] = N1[5];  N2new[5] = N1[4];
803   // change nodes in faces
804   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
806
807   // move the central node of biquadratic triangle
808   SMESH_MesherHelper helper( *GetMesh() );
809   for ( int is2nd = 0; is2nd < 2; ++is2nd )
810   {
811     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
812     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813     if ( nodes.size() < 7 )
814       continue;
815     helper.SetSubShape( tria->getshapeId() );
816     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
817     gp_Pnt xyz;
818     if ( F.IsNull() )
819     {
820       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821               SMESH_TNodeXYZ( nodes[4] ) +
822               SMESH_TNodeXYZ( nodes[5] )) / 3.;
823     }
824     else
825     {
826       bool checkUV;
827       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
830       TopLoc_Location loc;
831       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832       xyz = S->Value( uv.X(), uv.Y() );
833       xyz.Transform( loc );
834       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
835            nodes[6]->getshapeId() > 0 )
836         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
837     }
838     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
839   }
840   return true;
841 }
842
843 //=======================================================================
844 //function : findTriangles
845 //purpose  : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
847
848 static bool findTriangles(const SMDS_MeshNode *    theNode1,
849                           const SMDS_MeshNode *    theNode2,
850                           const SMDS_MeshElement*& theTria1,
851                           const SMDS_MeshElement*& theTria2)
852 {
853   if ( !theNode1 || !theNode2 ) return false;
854
855   theTria1 = theTria2 = 0;
856
857   set< const SMDS_MeshElement* > emap;
858   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
859   while (it->more()) {
860     const SMDS_MeshElement* elem = it->next();
861     if ( elem->NbCornerNodes() == 3 )
862       emap.insert( elem );
863   }
864   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
865   while (it->more()) {
866     const SMDS_MeshElement* elem = it->next();
867     if ( emap.count( elem )) {
868       if ( !theTria1 )
869       {
870         theTria1 = elem;
871       }
872       else  
873       {
874         theTria2 = elem;
875         // theTria1 must be element with minimum ID
876         if ( theTria2->GetID() < theTria1->GetID() )
877           std::swap( theTria2, theTria1 );
878         return true;
879       }
880     }
881   }
882   return false;
883 }
884
885 //=======================================================================
886 //function : InverseDiag
887 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
888 //           with ones built on the same 4 nodes but having other common link.
889 //           Return false if proper faces not found
890 //=======================================================================
891
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893                                     const SMDS_MeshNode * theNode2)
894 {
895   myLastCreatedElems.Clear();
896   myLastCreatedNodes.Clear();
897
898   const SMDS_MeshElement *tr1, *tr2;
899   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
900     return false;
901
902   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903   if (!F1) return false;
904   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905   if (!F2) return false;
906   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
908
909     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
911     //    |/ |                                    | \|
912     //  B +--+ 2                                B +--+ 2
913
914     // put nodes in array
915     // and find indices of 1,2 and of A in tr1 and of B in tr2
916     int i, iA1 = 0, i1 = 0;
917     const SMDS_MeshNode* aNodes1 [3];
918     SMDS_ElemIteratorPtr it;
919     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921       if ( aNodes1[ i ] == theNode1 )
922         iA1 = i; // node A in tr1
923       else if ( aNodes1[ i ] != theNode2 )
924         i1 = i;  // node 1
925     }
926     int iB2 = 0, i2 = 0;
927     const SMDS_MeshNode* aNodes2 [3];
928     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930       if ( aNodes2[ i ] == theNode2 )
931         iB2 = i; // node B in tr2
932       else if ( aNodes2[ i ] != theNode1 )
933         i2 = i;  // node 2
934     }
935
936     // nodes 1 and 2 should not be the same
937     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
938       return false;
939
940     // tr1: A->2
941     aNodes1[ iA1 ] = aNodes2[ i2 ];
942     // tr2: B->1
943     aNodes2[ iB2 ] = aNodes1[ i1 ];
944
945     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
947
948     return true;
949   }
950
951   // check case of quadratic faces
952   return InverseDiag(tr1,tr2);
953 }
954
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
958 //           fusion of triangles tr1 and tr2 having shared link on
959 //           theNode1 and theNode2
960 //=======================================================================
961
962 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
963                         const SMDS_MeshNode *    theNode1,
964                         const SMDS_MeshNode *    theNode2,
965                         const SMDS_MeshElement * tr1,
966                         const SMDS_MeshElement * tr2 )
967 {
968   if( tr1->NbNodes() != tr2->NbNodes() )
969     return false;
970   // find the 4-th node to insert into tr1
971   const SMDS_MeshNode* n4 = 0;
972   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
973   int i=0;
974   while ( !n4 && i<3 ) {
975     const SMDS_MeshNode * n = cast2Node( it->next() );
976     i++;
977     bool isDiag = ( n == theNode1 || n == theNode2 );
978     if ( !isDiag )
979       n4 = n;
980   }
981   // Make an array of nodes to be in a quadrangle
982   int iNode = 0, iFirstDiag = -1;
983   it = tr1->nodesIterator();
984   i=0;
985   while ( i<3 ) {
986     const SMDS_MeshNode * n = cast2Node( it->next() );
987     i++;
988     bool isDiag = ( n == theNode1 || n == theNode2 );
989     if ( isDiag ) {
990       if ( iFirstDiag < 0 )
991         iFirstDiag = iNode;
992       else if ( iNode - iFirstDiag == 1 )
993         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
994     }
995     else if ( n == n4 ) {
996       return false; // tr1 and tr2 should not have all the same nodes
997     }
998     theQuadNodes[ iNode++ ] = n;
999   }
1000   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001     theQuadNodes[ iNode ] = n4;
1002
1003   return true;
1004 }
1005
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 //           with a quadrangle built on the same 4 nodes.
1010 //           Return false if proper faces not found
1011 //=======================================================================
1012
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014                                    const SMDS_MeshNode * theNode2)
1015 {
1016   myLastCreatedElems.Clear();
1017   myLastCreatedNodes.Clear();
1018
1019   const SMDS_MeshElement *tr1, *tr2;
1020   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1021     return false;
1022
1023   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024   if (!F1) return false;
1025   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026   if (!F2) return false;
1027   SMESHDS_Mesh * aMesh = GetMeshDS();
1028
1029   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1031
1032     const SMDS_MeshNode* aNodes [ 4 ];
1033     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1034       return false;
1035
1036     const SMDS_MeshElement* newElem = 0;
1037     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038     myLastCreatedElems.Append(newElem);
1039     AddToSameGroups( newElem, tr1, aMesh );
1040     int aShapeId = tr1->getshapeId();
1041     if ( aShapeId )
1042       {
1043         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044       }
1045     aMesh->RemoveElement( tr1 );
1046     aMesh->RemoveElement( tr2 );
1047
1048     return true;
1049   }
1050
1051   // check case of quadratic faces
1052   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1053     return false;
1054   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055     return false;
1056
1057   //       5
1058   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1060   //    |   / |
1061   //  7 +  +  + 6
1062   //    | /9  |
1063   //    |/    |
1064   //  4 +--+--+ 3
1065   //       8
1066
1067   vector< const SMDS_MeshNode* > N1;
1068   vector< const SMDS_MeshNode* > N2;
1069   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1070     return false;
1071   // now we receive following N1 and N2 (using numeration as above image)
1072   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1073   // i.e. first nodes from both arrays determ new diagonal
1074
1075   const SMDS_MeshNode* aNodes[8];
1076   aNodes[0] = N1[0];
1077   aNodes[1] = N1[1];
1078   aNodes[2] = N2[0];
1079   aNodes[3] = N2[1];
1080   aNodes[4] = N1[3];
1081   aNodes[5] = N2[5];
1082   aNodes[6] = N2[3];
1083   aNodes[7] = N1[5];
1084
1085   const SMDS_MeshElement* newElem = 0;
1086   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088   myLastCreatedElems.Append(newElem);
1089   AddToSameGroups( newElem, tr1, aMesh );
1090   int aShapeId = tr1->getshapeId();
1091   if ( aShapeId )
1092     {
1093       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1094     }
1095   aMesh->RemoveElement( tr1 );
1096   aMesh->RemoveElement( tr2 );
1097
1098   // remove middle node (9)
1099   GetMeshDS()->RemoveNode( N1[4] );
1100
1101   return true;
1102 }
1103
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose  : Reverse theElement orientation
1107 //=======================================================================
1108
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1110 {
1111   myLastCreatedElems.Clear();
1112   myLastCreatedNodes.Clear();
1113
1114   if (!theElem)
1115     return false;
1116   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117   if ( !it || !it->more() )
1118     return false;
1119
1120   const SMDSAbs_ElementType type = theElem->GetType();
1121   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122     return false;
1123
1124   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1126   {
1127     const SMDS_VtkVolume* aPolyedre =
1128       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1129     if (!aPolyedre) {
1130       MESSAGE("Warning: bad volumic element");
1131       return false;
1132     }
1133     const int nbFaces = aPolyedre->NbFaces();
1134     vector<const SMDS_MeshNode *> poly_nodes;
1135     vector<int> quantities (nbFaces);
1136
1137     // reverse each face of the polyedre
1138     for (int iface = 1; iface <= nbFaces; iface++) {
1139       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140       quantities[iface - 1] = nbFaceNodes;
1141
1142       for (inode = nbFaceNodes; inode >= 1; inode--) {
1143         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144         poly_nodes.push_back(curNode);
1145       }
1146     }
1147     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1148   }
1149   else // other elements
1150   {
1151     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153     if ( interlace.empty() )
1154     {
1155       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1156     }
1157     else
1158     {
1159       SMDS_MeshCell::applyInterlace( interlace, nodes );
1160     }
1161     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1162   }
1163   return false;
1164 }
1165
1166 //================================================================================
1167 /*!
1168  * \brief Reorient faces.
1169  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170  * \param theDirection - desired direction of normal of \a theFace
1171  * \param theFace - one of \a theFaces that should be oriented according to
1172  *        \a theDirection and whose orientation defines orientation of other faces
1173  * \return number of reoriented faces.
1174  */
1175 //================================================================================
1176
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1178                                   const gp_Dir&            theDirection,
1179                                   const SMDS_MeshElement * theFace)
1180 {
1181   int nbReori = 0;
1182   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1183
1184   if ( theFaces.empty() )
1185   {
1186     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187     while ( fIt->more() )
1188       theFaces.insert( theFaces.end(), fIt->next() );
1189   }
1190
1191   // orient theFace according to theDirection
1192   gp_XYZ normal;
1193   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194   if ( normal * theDirection.XYZ() < 0 )
1195     nbReori += Reorient( theFace );
1196
1197   // Orient other faces
1198
1199   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200   TIDSortedElemSet avoidSet;
1201   set< SMESH_TLink > checkedLinks;
1202   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1203
1204   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205     theFaces.erase( theFace );
1206   startFaces.insert( theFace );
1207
1208   int nodeInd1, nodeInd2;
1209   const SMDS_MeshElement*           otherFace;
1210   vector< const SMDS_MeshElement* > facesNearLink;
1211   vector< std::pair< int, int > >   nodeIndsOfFace;
1212
1213   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214   while ( !startFaces.empty() )
1215   {
1216     startFace = startFaces.begin();
1217     theFace = *startFace;
1218     startFaces.erase( startFace );
1219     if ( !visitedFaces.insert( theFace ).second )
1220       continue;
1221
1222     avoidSet.clear();
1223     avoidSet.insert(theFace);
1224
1225     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1226
1227     const int nbNodes = theFace->NbCornerNodes();
1228     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1229     {
1230       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231       linkIt_isNew = checkedLinks.insert( link );
1232       if ( !linkIt_isNew.second )
1233       {
1234         // link has already been checked and won't be encountered more
1235         // if the group (theFaces) is manifold
1236         //checkedLinks.erase( linkIt_isNew.first );
1237       }
1238       else
1239       {
1240         facesNearLink.clear();
1241         nodeIndsOfFace.clear();
1242         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1243                                                              theFaces, avoidSet,
1244                                                              &nodeInd1, &nodeInd2 )))
1245           if ( otherFace != theFace)
1246           {
1247             facesNearLink.push_back( otherFace );
1248             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249             avoidSet.insert( otherFace );
1250           }
1251         if ( facesNearLink.size() > 1 )
1252         {
1253           // NON-MANIFOLD mesh shell !
1254           // select a face most co-directed with theFace,
1255           // other faces won't be visited this time
1256           gp_XYZ NF, NOF;
1257           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258           double proj, maxProj = -1;
1259           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261             if (( proj = Abs( NF * NOF )) > maxProj ) {
1262               maxProj = proj;
1263               otherFace = facesNearLink[i];
1264               nodeInd1  = nodeIndsOfFace[i].first;
1265               nodeInd2  = nodeIndsOfFace[i].second;
1266             }
1267           }
1268           // not to visit rejected faces
1269           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271               visitedFaces.insert( facesNearLink[i] );
1272         }
1273         else if ( facesNearLink.size() == 1 )
1274         {
1275           otherFace = facesNearLink[0];
1276           nodeInd1  = nodeIndsOfFace.back().first;
1277           nodeInd2  = nodeIndsOfFace.back().second;
1278         }
1279         if ( otherFace && otherFace != theFace)
1280         {
1281           // link must be reverse in otherFace if orientation ot otherFace
1282           // is same as that of theFace
1283           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1284           {
1285             nbReori += Reorient( otherFace );
1286           }
1287           startFaces.insert( otherFace );
1288         }
1289       }
1290       std::swap( link.first, link.second ); // reverse the link
1291     }
1292   }
1293   return nbReori;
1294 }
1295
1296 //================================================================================
1297 /*!
1298  * \brief Reorient faces basing on orientation of adjacent volumes.
1299  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300  * \param theVolumes - reference volumes.
1301  * \param theOutsideNormal - to orient faces to have their normal
1302  *        pointing either \a outside or \a inside the adjacent volumes.
1303  * \return number of reoriented faces.
1304  */
1305 //================================================================================
1306
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308                                       TIDSortedElemSet & theVolumes,
1309                                       const bool         theOutsideNormal)
1310 {
1311   int nbReori = 0;
1312
1313   SMDS_ElemIteratorPtr faceIt;
1314   if ( theFaces.empty() )
1315     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1316   else
1317     faceIt = elemSetIterator( theFaces );
1318
1319   vector< const SMDS_MeshNode* > faceNodes;
1320   TIDSortedElemSet checkedVolumes;
1321   set< const SMDS_MeshNode* > faceNodesSet;
1322   SMDS_VolumeTool volumeTool;
1323
1324   while ( faceIt->more() ) // loop on given faces
1325   {
1326     const SMDS_MeshElement* face = faceIt->next();
1327     if ( face->GetType() != SMDSAbs_Face )
1328       continue;
1329
1330     const size_t nbCornersNodes = face->NbCornerNodes();
1331     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1332
1333     checkedVolumes.clear();
1334     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335     while ( vIt->more() )
1336     {
1337       const SMDS_MeshElement* volume = vIt->next();
1338
1339       if ( !checkedVolumes.insert( volume ).second )
1340         continue;
1341       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342         continue;
1343
1344       // is volume adjacent?
1345       bool allNodesCommon = true;
1346       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348       if ( !allNodesCommon )
1349         continue;
1350
1351       // get nodes of a corresponding volume facet
1352       faceNodesSet.clear();
1353       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354       volumeTool.Set( volume );
1355       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356       if ( facetID < 0 ) continue;
1357       volumeTool.SetExternalNormal();
1358       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1359
1360       // compare order of faceNodes and facetNodes
1361       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1362       int iNN[2];
1363       for ( int i = 0; i < 2; ++i )
1364       {
1365         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367           if ( faceNodes[ iN ] == n )
1368           {
1369             iNN[ i ] = iN;
1370             break;
1371           }
1372       }
1373       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374       if ( isOutside != theOutsideNormal )
1375         nbReori += Reorient( face );
1376     }
1377   }  // loop on given faces
1378
1379   return nbReori;
1380 }
1381
1382 //=======================================================================
1383 //function : getBadRate
1384 //purpose  :
1385 //=======================================================================
1386
1387 static double getBadRate (const SMDS_MeshElement*               theElem,
1388                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1389 {
1390   SMESH::Controls::TSequenceOfXYZ P;
1391   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1392     return 1e100;
1393   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 }
1396
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose  : Cut quadrangles into triangles.
1400 //           theCrit is used to select a diagonal to cut
1401 //=======================================================================
1402
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1404                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1405 {
1406   myLastCreatedElems.Clear();
1407   myLastCreatedNodes.Clear();
1408
1409   if ( !theCrit.get() )
1410     return false;
1411
1412   SMESHDS_Mesh * aMesh = GetMeshDS();
1413
1414   Handle(Geom_Surface) surface;
1415   SMESH_MesherHelper   helper( *GetMesh() );
1416
1417   TIDSortedElemSet::iterator itElem;
1418   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1419   {
1420     const SMDS_MeshElement* elem = *itElem;
1421     if ( !elem || elem->GetType() != SMDSAbs_Face )
1422       continue;
1423     if ( elem->NbCornerNodes() != 4 )
1424       continue;
1425
1426     // retrieve element nodes
1427     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1428
1429     // compare two sets of possible triangles
1430     double aBadRate1, aBadRate2; // to what extent a set is bad
1431     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434
1435     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1438
1439     const int aShapeId = FindShape( elem );
1440     const SMDS_MeshElement* newElem1 = 0;
1441     const SMDS_MeshElement* newElem2 = 0;
1442
1443     if ( !elem->IsQuadratic() ) // split liner quadrangle
1444     {
1445       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447       if ( aBadRate1 <= aBadRate2 ) {
1448         // tr1 + tr2 is better
1449         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451       }
1452       else {
1453         // tr3 + tr4 is better
1454         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456       }
1457     }
1458     else // split quadratic quadrangle
1459     {
1460       helper.SetIsQuadratic( true );
1461       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1462
1463       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464       if ( aNodes.size() == 9 )
1465       {
1466         helper.SetIsBiQuadratic( true );
1467         if ( aBadRate1 <= aBadRate2 )
1468           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1469         else
1470           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1471       }
1472       // create a new element
1473       if ( aBadRate1 <= aBadRate2 ) {
1474         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476       }
1477       else {
1478         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1480       }
1481     } // quadratic case
1482
1483     // care of a new element
1484
1485     myLastCreatedElems.Append(newElem1);
1486     myLastCreatedElems.Append(newElem2);
1487     AddToSameGroups( newElem1, elem, aMesh );
1488     AddToSameGroups( newElem2, elem, aMesh );
1489
1490     // put a new triangle on the same shape
1491     if ( aShapeId )
1492       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1494
1495     aMesh->RemoveElement( elem );
1496   }
1497   return true;
1498 }
1499
1500 //=======================================================================
1501 /*!
1502  * \brief Split each of given quadrangles into 4 triangles.
1503  * \param theElems - The faces to be splitted. If empty all faces are split.
1504  */
1505 //=======================================================================
1506
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1508 {
1509   myLastCreatedElems.Clear();
1510   myLastCreatedNodes.Clear();
1511
1512   SMESH_MesherHelper helper( *GetMesh() );
1513   helper.SetElementsOnShape( true );
1514
1515   SMDS_ElemIteratorPtr faceIt;
1516   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517   else                    faceIt = elemSetIterator( theElems );
1518
1519   bool   checkUV;
1520   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1521   gp_XYZ xyz[9];
1522   vector< const SMDS_MeshNode* > nodes;
1523   SMESHDS_SubMesh*               subMeshDS = 0;
1524   TopoDS_Face                    F;
1525   Handle(Geom_Surface)           surface;
1526   TopLoc_Location                loc;
1527
1528   while ( faceIt->more() )
1529   {
1530     const SMDS_MeshElement* quad = faceIt->next();
1531     if ( !quad || quad->NbCornerNodes() != 4 )
1532       continue;
1533
1534     // get a surface the quad is on
1535
1536     if ( quad->getshapeId() < 1 )
1537     {
1538       F.Nullify();
1539       helper.SetSubShape( 0 );
1540       subMeshDS = 0;
1541     }
1542     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543     {
1544       helper.SetSubShape( quad->getshapeId() );
1545       if ( !helper.GetSubShape().IsNull() &&
1546            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547       {
1548         F = TopoDS::Face( helper.GetSubShape() );
1549         surface = BRep_Tool::Surface( F, loc );
1550         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1551       }
1552       else
1553       {
1554         helper.SetSubShape( 0 );
1555         subMeshDS = 0;
1556       }
1557     }
1558
1559     // create a central node
1560
1561     const SMDS_MeshNode* nCentral;
1562     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563
1564     if ( nodes.size() == 9 )
1565     {
1566       nCentral = nodes.back();
1567     }
1568     else
1569     {
1570       size_t iN = 0;
1571       if ( F.IsNull() )
1572       {
1573         for ( ; iN < nodes.size(); ++iN )
1574           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1575
1576         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578
1579         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580                                    xyz[0], xyz[1], xyz[2], xyz[3],
1581                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1582       }
1583       else
1584       {
1585         for ( ; iN < nodes.size(); ++iN )
1586           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587
1588         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590
1591         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592                                   uv[0], uv[1], uv[2], uv[3],
1593                                   uv[4], uv[5], uv[6], uv[7] );
1594
1595         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1596         xyz[ 8 ] = p.XYZ();
1597       }
1598
1599       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600                                  uv[8].X(), uv[8].Y() );
1601       myLastCreatedNodes.Append( nCentral );
1602     }
1603
1604     // create 4 triangles
1605
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1612
1613     for ( int i = 0; i < 4; ++i )
1614     {
1615       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616                                                nodes[(i+1)%4],
1617                                                nCentral );
1618       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619       myLastCreatedElems.Append( tria );
1620     }
1621   }
1622 }
1623
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose  : Find better diagonal for cutting.
1627 //=======================================================================
1628
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1630                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1631 {
1632   myLastCreatedElems.Clear();
1633   myLastCreatedNodes.Clear();
1634
1635   if (!theCrit.get())
1636     return -1;
1637
1638   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639     return -1;
1640
1641   if( theQuad->NbNodes()==4 ||
1642       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1643
1644     // retrieve element nodes
1645     const SMDS_MeshNode* aNodes [4];
1646     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1647     int i = 0;
1648     //while (itN->more())
1649     while (i<4) {
1650       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1651     }
1652     // compare two sets of possible triangles
1653     double aBadRate1, aBadRate2; // to what extent a set is bad
1654     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1657
1658     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664       return 1; // diagonal 1-3
1665
1666     return 2; // diagonal 2-4
1667   }
1668   return -1;
1669 }
1670
1671 namespace
1672 {
1673   // Methods of splitting volumes into tetra
1674
1675   const int theHexTo5_1[5*4+1] =
1676     {
1677       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1678     };
1679   const int theHexTo5_2[5*4+1] =
1680     {
1681       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1682     };
1683   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1684
1685   const int theHexTo6_1[6*4+1] =
1686     {
1687       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
1688     };
1689   const int theHexTo6_2[6*4+1] =
1690     {
1691       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
1692     };
1693   const int theHexTo6_3[6*4+1] =
1694     {
1695       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
1696     };
1697   const int theHexTo6_4[6*4+1] =
1698     {
1699       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
1700     };
1701   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1702
1703   const int thePyraTo2_1[2*4+1] =
1704     {
1705       0, 1, 2, 4,    0, 2, 3, 4,   -1
1706     };
1707   const int thePyraTo2_2[2*4+1] =
1708     {
1709       1, 2, 3, 4,    1, 3, 0, 4,   -1
1710     };
1711   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1712
1713   const int thePentaTo3_1[3*4+1] =
1714     {
1715       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1716     };
1717   const int thePentaTo3_2[3*4+1] =
1718     {
1719       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1720     };
1721   const int thePentaTo3_3[3*4+1] =
1722     {
1723       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1724     };
1725   const int thePentaTo3_4[3*4+1] =
1726     {
1727       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1728     };
1729   const int thePentaTo3_5[3*4+1] =
1730     {
1731       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1732     };
1733   const int thePentaTo3_6[3*4+1] =
1734     {
1735       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1736     };
1737   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1739
1740   // Methods of splitting hexahedron into prisms
1741
1742   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1743     {
1744       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
1745     };
1746   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1747     {
1748       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
1749     };
1750   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1751     {
1752       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
1753     };
1754
1755   const int theHexTo2Prisms_BT_1[6*2+1] =
1756     {
1757       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1758     };
1759   const int theHexTo2Prisms_BT_2[6*2+1] =
1760     {
1761       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1762     };
1763   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1764
1765   const int theHexTo2Prisms_LR_1[6*2+1] =
1766     {
1767       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1768     };
1769   const int theHexTo2Prisms_LR_2[6*2+1] =
1770     {
1771       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1772     };
1773   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1774
1775   const int theHexTo2Prisms_FB_1[6*2+1] =
1776     {
1777       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1778     };
1779   const int theHexTo2Prisms_FB_2[6*2+1] =
1780     {
1781       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1782     };
1783   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784
1785
1786   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787   {
1788     int _n1, _n2, _n3;
1789     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1792                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1793   };
1794   struct TSplitMethod
1795   {
1796     int        _nbSplits;
1797     int        _nbCorners;
1798     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1800     bool       _ownConn;      //!< to delete _connectivity in destructor
1801     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1802
1803     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806     bool hasFacet( const TTriangleFacet& facet ) const
1807     {
1808       if ( _nbCorners == 4 )
1809       {
1810         const int* tetConn = _connectivity;
1811         for ( ; tetConn[0] >= 0; tetConn += 4 )
1812           if (( facet.contains( tetConn[0] ) +
1813                 facet.contains( tetConn[1] ) +
1814                 facet.contains( tetConn[2] ) +
1815                 facet.contains( tetConn[3] )) == 3 )
1816             return true;
1817       }
1818       else // prism, _nbCorners == 6
1819       {
1820         const int* prismConn = _connectivity;
1821         for ( ; prismConn[0] >= 0; prismConn += 6 )
1822         {
1823           if (( facet.contains( prismConn[0] ) &&
1824                 facet.contains( prismConn[1] ) &&
1825                 facet.contains( prismConn[2] ))
1826               ||
1827               ( facet.contains( prismConn[3] ) &&
1828                 facet.contains( prismConn[4] ) &&
1829                 facet.contains( prismConn[5] )))
1830             return true;
1831         }
1832       }
1833       return false;
1834     }
1835   };
1836
1837   //=======================================================================
1838   /*!
1839    * \brief return TSplitMethod for the given element to split into tetrahedra
1840    */
1841   //=======================================================================
1842
1843   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1844   {
1845     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1846
1847     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848     // an edge and a face barycenter; tertaherdons are based on triangles and
1849     // a volume barycenter
1850     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1851
1852     // Find out how adjacent volumes are split
1853
1854     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1857     {
1858       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860       if ( nbNodes < 4 ) continue;
1861
1862       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863       const int* nInd = vol.GetFaceNodesIndices( iF );
1864       if ( nbNodes == 4 )
1865       {
1866         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1870       }
1871       else
1872       {
1873         int iCom = 0; // common node of triangle faces to split into
1874         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1875         {
1876           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1877                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1880                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1881                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1883           {
1884             triaSplits.push_back( t012 );
1885             triaSplits.push_back( t023 );
1886             break;
1887           }
1888         }
1889       }
1890       if ( !triaSplits.empty() )
1891         hasAdjacentSplits = true;
1892     }
1893
1894     // Among variants of split method select one compliant with adjacent volumes
1895
1896     TSplitMethod method;
1897     if ( !vol.Element()->IsPoly() && !is24TetMode )
1898     {
1899       int nbVariants = 2, nbTet = 0;
1900       const int** connVariants = 0;
1901       switch ( vol.Element()->GetEntityType() )
1902       {
1903       case SMDSEntity_Hexa:
1904       case SMDSEntity_Quad_Hexa:
1905       case SMDSEntity_TriQuad_Hexa:
1906         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907           connVariants = theHexTo5, nbTet = 5;
1908         else
1909           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1910         break;
1911       case SMDSEntity_Pyramid:
1912       case SMDSEntity_Quad_Pyramid:
1913         connVariants = thePyraTo2;  nbTet = 2;
1914         break;
1915       case SMDSEntity_Penta:
1916       case SMDSEntity_Quad_Penta:
1917       case SMDSEntity_BiQuad_Penta:
1918         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1919         break;
1920       default:
1921         nbVariants = 0;
1922       }
1923       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1924       {
1925         // check method compliancy with adjacent tetras,
1926         // all found splits must be among facets of tetras described by this method
1927         method = TSplitMethod( nbTet, connVariants[variant] );
1928         if ( hasAdjacentSplits && method._nbSplits > 0 )
1929         {
1930           bool facetCreated = true;
1931           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1932           {
1933             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1934             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1935               facetCreated = method.hasFacet( *facet );
1936           }
1937           if ( !facetCreated )
1938             method = TSplitMethod(0); // incompatible method
1939         }
1940       }
1941     }
1942     if ( method._nbSplits < 1 )
1943     {
1944       // No standard method is applicable, use a generic solution:
1945       // each facet of a volume is split into triangles and
1946       // each of triangles and a volume barycenter form a tetrahedron.
1947
1948       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1949
1950       int* connectivity = new int[ maxTetConnSize + 1 ];
1951       method._connectivity = connectivity;
1952       method._ownConn = true;
1953       method._baryNode = !isHex27; // to create central node or not
1954
1955       int connSize = 0;
1956       int baryCenInd = vol.NbNodes() - int( isHex27 );
1957       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1958       {
1959         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1960         const int*   nInd = vol.GetFaceNodesIndices( iF );
1961         // find common node of triangle facets of tetra to create
1962         int iCommon = 0; // index in linear numeration
1963         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1964         if ( !triaSplits.empty() )
1965         {
1966           // by found facets
1967           const TTriangleFacet* facet = &triaSplits.front();
1968           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1969             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1970                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1971               break;
1972         }
1973         else if ( nbNodes > 3 && !is24TetMode )
1974         {
1975           // find the best method of splitting into triangles by aspect ratio
1976           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1977           map< double, int > badness2iCommon;
1978           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1979           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1980           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1981           {
1982             double badness = 0;
1983             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1984             {
1985               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1986                                       nodes[ iQ*((iLast-1)%nbNodes)],
1987                                       nodes[ iQ*((iLast  )%nbNodes)]);
1988               badness += getBadRate( &tria, aspectRatio );
1989             }
1990             badness2iCommon.insert( make_pair( badness, iCommon ));
1991           }
1992           // use iCommon with lowest badness
1993           iCommon = badness2iCommon.begin()->second;
1994         }
1995         if ( iCommon >= nbNodes )
1996           iCommon = 0; // something wrong
1997
1998         // fill connectivity of tetrahedra based on a current face
1999         int nbTet = nbNodes - 2;
2000         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2001         {
2002           int faceBaryCenInd;
2003           if ( isHex27 )
2004           {
2005             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2006             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007           }
2008           else
2009           {
2010             method._faceBaryNode[ iF ] = 0;
2011             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2012           }
2013           nbTet = nbNodes;
2014           for ( int i = 0; i < nbTet; ++i )
2015           {
2016             int i1 = i, i2 = (i+1) % nbNodes;
2017             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2018             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2019             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2020             connectivity[ connSize++ ] = faceBaryCenInd;
2021             connectivity[ connSize++ ] = baryCenInd;
2022           }
2023         }
2024         else
2025         {
2026           for ( int i = 0; i < nbTet; ++i )
2027           {
2028             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2029             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2030             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2031             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2032             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2033             connectivity[ connSize++ ] = baryCenInd;
2034           }
2035         }
2036         method._nbSplits += nbTet;
2037
2038       } // loop on volume faces
2039
2040       connectivity[ connSize++ ] = -1;
2041
2042     } // end of generic solution
2043
2044     return method;
2045   }
2046   //=======================================================================
2047   /*!
2048    * \brief return TSplitMethod to split haxhedron into prisms
2049    */
2050   //=======================================================================
2051
2052   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2053                                     const int        methodFlags,
2054                                     const int        facetToSplit)
2055   {
2056     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2057     // B, T, L, B, R, F
2058     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2059
2060     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2061     {
2062       static TSplitMethod to4methods[4]; // order BT, LR, FB
2063       if ( to4methods[iF]._nbSplits == 0 )
2064       {
2065         switch ( iF ) {
2066         case 0:
2067           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2068           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2069           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2070           break;
2071         case 1:
2072           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2073           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2074           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2075           break;
2076         case 2:
2077           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2078           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2079           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2080           break;
2081         default: return to4methods[3];
2082         }
2083         to4methods[iF]._nbSplits  = 4;
2084         to4methods[iF]._nbCorners = 6;
2085       }
2086       return to4methods[iF];
2087     }
2088     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2089
2090     TSplitMethod method;
2091
2092     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2093
2094     const int nbVariants = 2, nbSplits = 2;
2095     const int** connVariants = 0;
2096     switch ( iF ) {
2097     case 0: connVariants = theHexTo2Prisms_BT; break;
2098     case 1: connVariants = theHexTo2Prisms_LR; break;
2099     case 2: connVariants = theHexTo2Prisms_FB; break;
2100     default: return method;
2101     }
2102
2103     // look for prisms adjacent via facetToSplit and an opposite one
2104     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2105     {
2106       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2107       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2108       if ( nbNodes != 4 ) return method;
2109
2110       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2111       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2112       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2113       TTriangleFacet* t;
2114       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2115         t = &t012;
2116       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2117         t = &t123;
2118       else
2119         continue;
2120
2121       // there are adjacent prism
2122       for ( int variant = 0; variant < nbVariants; ++variant )
2123       {
2124         // check method compliancy with adjacent prisms,
2125         // the found prism facets must be among facets of prisms described by current method
2126         method._nbSplits     = nbSplits;
2127         method._nbCorners    = 6;
2128         method._connectivity = connVariants[ variant ];
2129         if ( method.hasFacet( *t ))
2130           return method;
2131       }
2132     }
2133
2134     // No adjacent prisms. Select a variant with a best aspect ratio.
2135
2136     double badness[2] = { 0., 0. };
2137     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2138     const SMDS_MeshNode** nodes = vol.GetNodes();
2139     for ( int variant = 0; variant < nbVariants; ++variant )
2140       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2141       {
2142         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2143         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2144
2145         method._connectivity = connVariants[ variant ];
2146         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2147         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2148         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2149
2150         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2151                                 nodes[ t->_n2 ],
2152                                 nodes[ t->_n3 ] );
2153         badness[ variant ] += getBadRate( &tria, aspectRatio );
2154       }
2155     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2156
2157     method._nbSplits     = nbSplits;
2158     method._nbCorners    = 6;
2159     method._connectivity = connVariants[ iBetter ];
2160
2161     return method;
2162   }
2163
2164   //================================================================================
2165   /*!
2166    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2167    */
2168   //================================================================================
2169
2170   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2171                                        const SMDSAbs_GeometryType geom ) const
2172   {
2173     // find the tetrahedron including the three nodes of facet
2174     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2175     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2176     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2177     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2178     while ( volIt1->more() )
2179     {
2180       const SMDS_MeshElement* v = volIt1->next();
2181       if ( v->GetGeomType() != geom )
2182         continue;
2183       const int lastCornerInd = v->NbCornerNodes() - 1;
2184       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2185         continue; // medium node not allowed
2186       const int ind2 = v->GetNodeIndex( n2 );
2187       if ( ind2 < 0 || lastCornerInd < ind2 )
2188         continue;
2189       const int ind3 = v->GetNodeIndex( n3 );
2190       if ( ind3 < 0 || lastCornerInd < ind3 )
2191         continue;
2192       return true;
2193     }
2194     return false;
2195   }
2196
2197   //=======================================================================
2198   /*!
2199    * \brief A key of a face of volume
2200    */
2201   //=======================================================================
2202
2203   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2204   {
2205     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2206     {
2207       TIDSortedNodeSet sortedNodes;
2208       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2209       int nbNodes = vol.NbFaceNodes( iF );
2210       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2211       for ( int i = 0; i < nbNodes; i += iQ )
2212         sortedNodes.insert( fNodes[i] );
2213       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2214       first.first   = (*(n++))->GetID();
2215       first.second  = (*(n++))->GetID();
2216       second.first  = (*(n++))->GetID();
2217       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2218     }
2219   };
2220 } // namespace
2221
2222 //=======================================================================
2223 //function : SplitVolumes
2224 //purpose  : Split volume elements into tetrahedra or prisms.
2225 //           If facet ID < 0, element is split into tetrahedra,
2226 //           else a hexahedron is split into prisms so that the given facet is
2227 //           split into triangles
2228 //=======================================================================
2229
2230 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2231                                      const int            theMethodFlags)
2232 {
2233   SMDS_VolumeTool    volTool;
2234   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2235   fHelper.ToFixNodeParameters( true );
2236
2237   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2238   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2239
2240   SMESH_SequenceOfElemPtr newNodes, newElems;
2241
2242   // map face of volume to it's baricenrtic node
2243   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2244   double bc[3];
2245   vector<const SMDS_MeshElement* > splitVols;
2246
2247   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2248   for ( ; elem2facet != theElems.end(); ++elem2facet )
2249   {
2250     const SMDS_MeshElement* elem = elem2facet->first;
2251     const int       facetToSplit = elem2facet->second;
2252     if ( elem->GetType() != SMDSAbs_Volume )
2253       continue;
2254     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2255     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2256       continue;
2257
2258     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2259
2260     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2261                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2262                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2263     if ( splitMethod._nbSplits < 1 ) continue;
2264
2265     // find submesh to add new tetras to
2266     if ( !subMesh || !subMesh->Contains( elem ))
2267     {
2268       int shapeID = FindShape( elem );
2269       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2270       subMesh = GetMeshDS()->MeshElements( shapeID );
2271     }
2272     int iQ;
2273     if ( elem->IsQuadratic() )
2274     {
2275       iQ = 2;
2276       // add quadratic links to the helper
2277       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2278       {
2279         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2280         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2281         for ( int iN = 0; iN < nbN; iN += iQ )
2282           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2283       }
2284       helper.SetIsQuadratic( true );
2285     }
2286     else
2287     {
2288       iQ = 1;
2289       helper.SetIsQuadratic( false );
2290     }
2291     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2292                                         volTool.GetNodes() + elem->NbNodes() );
2293     helper.SetElementsOnShape( true );
2294     if ( splitMethod._baryNode )
2295     {
2296       // make a node at barycenter
2297       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2298       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2299       nodes.push_back( gcNode );
2300       newNodes.Append( gcNode );
2301     }
2302     if ( !splitMethod._faceBaryNode.empty() )
2303     {
2304       // make or find baricentric nodes of faces
2305       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2306       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2307       {
2308         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2309           volFace2BaryNode.insert
2310           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2311         if ( !f_n->second )
2312         {
2313           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2314           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2315         }
2316         nodes.push_back( iF_n->second = f_n->second );
2317       }
2318     }
2319
2320     // make new volumes
2321     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2322     const int* volConn = splitMethod._connectivity;
2323     if ( splitMethod._nbCorners == 4 ) // tetra
2324       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2325         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2326                                                             nodes[ volConn[1] ],
2327                                                             nodes[ volConn[2] ],
2328                                                             nodes[ volConn[3] ]));
2329     else // prisms
2330       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2331         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2332                                                             nodes[ volConn[1] ],
2333                                                             nodes[ volConn[2] ],
2334                                                             nodes[ volConn[3] ],
2335                                                             nodes[ volConn[4] ],
2336                                                             nodes[ volConn[5] ]));
2337
2338     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2339
2340     // Split faces on sides of the split volume
2341
2342     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2343     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2344     {
2345       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2346       if ( nbNodes < 4 ) continue;
2347
2348       // find an existing face
2349       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2350                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2351       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2352                                                                        /*noMedium=*/false))
2353       {
2354         // make triangles
2355         helper.SetElementsOnShape( false );
2356         vector< const SMDS_MeshElement* > triangles;
2357
2358         // find submesh to add new triangles in
2359         if ( !fSubMesh || !fSubMesh->Contains( face ))
2360         {
2361           int shapeID = FindShape( face );
2362           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2363         }
2364         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2365         if ( iF_n != splitMethod._faceBaryNode.end() )
2366         {
2367           const SMDS_MeshNode *baryNode = iF_n->second;
2368           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2369           {
2370             const SMDS_MeshNode* n1 = fNodes[iN];
2371             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2372             const SMDS_MeshNode *n3 = baryNode;
2373             if ( !volTool.IsFaceExternal( iF ))
2374               swap( n2, n3 );
2375             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2376           }
2377           if ( fSubMesh ) // update position of the bary node on geometry
2378           {
2379             if ( subMesh )
2380               subMesh->RemoveNode( baryNode, false );
2381             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2382             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2383             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2384             {
2385               fHelper.SetSubShape( s );
2386               gp_XY uv( 1e100, 1e100 );
2387               double distXYZ[4];
2388               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2389                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2390                    uv.X() < 1e100 )
2391               {
2392                 // node is too far from the surface
2393                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2394                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2395                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2396               }
2397             }
2398           }
2399         }
2400         else
2401         {
2402           // among possible triangles create ones described by split method
2403           const int* nInd = volTool.GetFaceNodesIndices( iF );
2404           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2405           int iCom = 0; // common node of triangle faces to split into
2406           list< TTriangleFacet > facets;
2407           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2408           {
2409             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2412             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2413                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2414                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2415             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2416             {
2417               facets.push_back( t012 );
2418               facets.push_back( t023 );
2419               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2420                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2421                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2422                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2423               break;
2424             }
2425           }
2426           list< TTriangleFacet >::iterator facet = facets.begin();
2427           if ( facet == facets.end() )
2428             break;
2429           for ( ; facet != facets.end(); ++facet )
2430           {
2431             if ( !volTool.IsFaceExternal( iF ))
2432               swap( facet->_n2, facet->_n3 );
2433             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2434                                                  volNodes[ facet->_n2 ],
2435                                                  volNodes[ facet->_n3 ]));
2436           }
2437         }
2438         for ( size_t i = 0; i < triangles.size(); ++i )
2439         {
2440           if ( !triangles[ i ]) continue;
2441           if ( fSubMesh )
2442             fSubMesh->AddElement( triangles[ i ]);
2443           newElems.Append( triangles[ i ]);
2444         }
2445         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2446         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2447
2448       } // while a face based on facet nodes exists
2449     } // loop on volume faces to split them into triangles
2450
2451     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2452
2453     if ( geomType == SMDSEntity_TriQuad_Hexa )
2454     {
2455       // remove medium nodes that could become free
2456       for ( int i = 20; i < volTool.NbNodes(); ++i )
2457         if ( volNodes[i]->NbInverseElements() == 0 )
2458           GetMeshDS()->RemoveNode( volNodes[i] );
2459     }
2460   } // loop on volumes to split
2461
2462   myLastCreatedNodes = newNodes;
2463   myLastCreatedElems = newElems;
2464 }
2465
2466 //=======================================================================
2467 //function : GetHexaFacetsToSplit
2468 //purpose  : For hexahedra that will be split into prisms, finds facets to
2469 //           split into triangles. Only hexahedra adjacent to the one closest
2470 //           to theFacetNormal.Location() are returned.
2471 //param [in,out] theHexas - the hexahedra
2472 //param [in]     theFacetNormal - facet normal
2473 //param [out]    theFacets - the hexahedra and found facet IDs
2474 //=======================================================================
2475
2476 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2477                                              const gp_Ax1&     theFacetNormal,
2478                                              TFacetOfElem &    theFacets)
2479 {
2480   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2481
2482   // Find a hexa closest to the location of theFacetNormal
2483
2484   const SMDS_MeshElement* startHex;
2485   {
2486     // get SMDS_ElemIteratorPtr on theHexas
2487     typedef const SMDS_MeshElement*                                      TValue;
2488     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2489     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2490     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2491     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2492     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2493       ( new TElemSetIter( theHexas.begin(),
2494                           theHexas.end(),
2495                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2496
2497     SMESH_ElementSearcher* searcher =
2498       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2499
2500     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2501
2502     delete searcher;
2503
2504     if ( !startHex )
2505       throw SALOME_Exception( THIS_METHOD "startHex not found");
2506   }
2507
2508   // Select a facet of startHex by theFacetNormal
2509
2510   SMDS_VolumeTool vTool( startHex );
2511   double norm[3], dot, maxDot = 0;
2512   int facetID = -1;
2513   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2514     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2515     {
2516       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2517       if ( dot > maxDot )
2518       {
2519         facetID = iF;
2520         maxDot = dot;
2521       }
2522     }
2523   if ( facetID < 0 )
2524     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2525
2526   // Fill theFacets starting from facetID of startHex
2527
2528   // facets used for searching of volumes adjacent to already treated ones
2529   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2530   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2531   TFacetMap facetsToCheck;
2532
2533   set<const SMDS_MeshNode*> facetNodes;
2534   const SMDS_MeshElement*   curHex;
2535
2536   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2537
2538   while ( startHex )
2539   {
2540     // move in two directions from startHex via facetID
2541     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2542     {
2543       curHex       = startHex;
2544       int curFacet = facetID;
2545       if ( is2nd ) // do not treat startHex twice
2546       {
2547         vTool.Set( curHex );
2548         if ( vTool.IsFreeFace( curFacet, &curHex ))
2549         {
2550           curHex = 0;
2551         }
2552         else
2553         {
2554           vTool.GetFaceNodes( curFacet, facetNodes );
2555           vTool.Set( curHex );
2556           curFacet = vTool.GetFaceIndex( facetNodes );
2557         }
2558       }
2559       while ( curHex )
2560       {
2561         // store a facet to split
2562         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2563         {
2564           theFacets.insert( make_pair( curHex, -1 ));
2565           break;
2566         }
2567         if ( !allHex && !theHexas.count( curHex ))
2568           break;
2569
2570         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2571           theFacets.insert( make_pair( curHex, curFacet ));
2572         if ( !facetIt2isNew.second )
2573           break;
2574
2575         // remember not-to-split facets in facetsToCheck
2576         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2577         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2578         {
2579           if ( iF == curFacet && iF == oppFacet )
2580             continue;
2581           TVolumeFaceKey facetKey ( vTool, iF );
2582           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2583           pair< TFacetMap::iterator, bool > it2isnew =
2584             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2585           if ( !it2isnew.second )
2586             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2587         }
2588         // pass to a volume adjacent via oppFacet
2589         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2590         {
2591           curHex = 0;
2592         }
2593         else
2594         {
2595           // get a new curFacet
2596           vTool.GetFaceNodes( oppFacet, facetNodes );
2597           vTool.Set( curHex );
2598           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2599         }
2600       }
2601     } // move in two directions from startHex via facetID
2602
2603     // Find a new startHex by facetsToCheck
2604
2605     startHex = 0;
2606     facetID  = -1;
2607     TFacetMap::iterator fIt = facetsToCheck.begin();
2608     while ( !startHex && fIt != facetsToCheck.end() )
2609     {
2610       const TElemFacets&  elemFacets = fIt->second;
2611       const SMDS_MeshElement*    hex = elemFacets.first->first;
2612       int                 splitFacet = elemFacets.first->second;
2613       int               lateralFacet = elemFacets.second;
2614       facetsToCheck.erase( fIt );
2615       fIt = facetsToCheck.begin();
2616
2617       vTool.Set( hex );
2618       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2619            curHex->GetGeomType() != SMDSGeom_HEXA )
2620         continue;
2621       if ( !allHex && !theHexas.count( curHex ))
2622         continue;
2623
2624       startHex = curHex;
2625
2626       // find a facet of startHex to split
2627
2628       set<const SMDS_MeshNode*> lateralNodes;
2629       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2630       vTool.GetFaceNodes( splitFacet,   facetNodes );
2631       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2632       vTool.Set( startHex );
2633       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2634
2635       // look for a facet of startHex having common nodes with facetNodes
2636       // but not lateralFacet
2637       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2638       {
2639         if ( iF == lateralFacet )
2640           continue;
2641         int nbCommonNodes = 0;
2642         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2643         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2644           nbCommonNodes += facetNodes.count( nn[ iN ]);
2645
2646         if ( nbCommonNodes >= 2 )
2647         {
2648           facetID = iF;
2649           break;
2650         }
2651       }
2652       if ( facetID < 0 )
2653         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2654     }
2655   } //   while ( startHex )
2656
2657   return;
2658 }
2659
2660 namespace
2661 {
2662   //================================================================================
2663   /*!
2664    * \brief Selects nodes of several elements according to a given interlace
2665    *  \param [in] srcNodes - nodes to select from
2666    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2667    *  \param [in] interlace - indices of nodes for all elements
2668    *  \param [in] nbElems - nb of elements
2669    *  \param [in] nbNodes - nb of nodes in each element
2670    *  \param [in] mesh - the mesh
2671    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2672    *  \param [in] type - type of elements to look for
2673    */
2674   //================================================================================
2675
2676   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2677                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2678                     const int*                            interlace,
2679                     const int                             nbElems,
2680                     const int                             nbNodes,
2681                     SMESHDS_Mesh*                         mesh = 0,
2682                     list< const SMDS_MeshElement* >*      elemQueue=0,
2683                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2684   {
2685     for ( int iE = 0; iE < nbElems; ++iE )
2686     {
2687       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2688       const int*                         select = & interlace[iE*nbNodes];
2689       elemNodes.resize( nbNodes );
2690       for ( int iN = 0; iN < nbNodes; ++iN )
2691         elemNodes[iN] = srcNodes[ select[ iN ]];
2692     }
2693     const SMDS_MeshElement* e;
2694     if ( elemQueue )
2695       for ( int iE = 0; iE < nbElems; ++iE )
2696         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2697           elemQueue->push_back( e );
2698   }
2699 }
2700
2701 //=======================================================================
2702 /*
2703  * Split bi-quadratic elements into linear ones without creation of additional nodes
2704  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2705  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2706  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2707  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2708  *   will be split in order to keep the mesh conformal.
2709  *  \param elems - elements to split
2710  */
2711 //=======================================================================
2712
2713 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2714 {
2715   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2716   vector<const SMDS_MeshElement* > splitElems;
2717   list< const SMDS_MeshElement* > elemQueue;
2718   list< const SMDS_MeshElement* >::iterator elemIt;
2719
2720   SMESHDS_Mesh * mesh = GetMeshDS();
2721   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2722   int nbElems, nbNodes;
2723
2724   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2725   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2726   {
2727     elemQueue.clear();
2728     elemQueue.push_back( *elemSetIt );
2729     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2730     {
2731       const SMDS_MeshElement* elem = *elemIt;
2732       switch( elem->GetEntityType() )
2733       {
2734       case SMDSEntity_TriQuad_Hexa: // HEX27
2735       {
2736         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2737         nbElems  = nbNodes = 8;
2738         elemType = & hexaType;
2739
2740         // get nodes for new elements
2741         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2742                                  { 1,9,20,8,    17,22,26,21 },
2743                                  { 2,10,20,9,   18,23,26,22 },
2744                                  { 3,11,20,10,  19,24,26,23 },
2745                                  { 16,21,26,24, 4,12,25,15  },
2746                                  { 17,22,26,21, 5,13,25,12  },
2747                                  { 18,23,26,22, 6,14,25,13  },
2748                                  { 19,24,26,23, 7,15,25,14  }};
2749         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2750
2751         // add boundary faces to elemQueue
2752         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2753                                  { 4,5,6,7, 12,13,14,15, 25 },
2754                                  { 0,1,5,4, 8,17,12,16,  21 },
2755                                  { 1,2,6,5, 9,18,13,17,  22 },
2756                                  { 2,3,7,6, 10,19,14,18, 23 },
2757                                  { 3,0,4,7, 11,16,15,19, 24 }};
2758         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2759
2760         // add boundary segments to elemQueue
2761         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2762                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2763                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2764         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2765         break;
2766       }
2767       case SMDSEntity_BiQuad_Triangle: // TRIA7
2768       {
2769         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770         nbElems = 3;
2771         nbNodes = 4;
2772         elemType = & quadType;
2773
2774         // get nodes for new elements
2775         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2776         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2777
2778         // add boundary segments to elemQueue
2779         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2780         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2781         break;
2782       }
2783       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2784       {
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 4;
2787         nbNodes = 4;
2788         elemType = & quadType;
2789
2790         // get nodes for new elements
2791         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2792         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2793
2794         // add boundary segments to elemQueue
2795         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2796         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2797         break;
2798       }
2799       case SMDSEntity_Quad_Edge:
2800       {
2801         if ( elemIt == elemQueue.begin() )
2802           continue; // an elem is in theElems
2803         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2804         nbElems = 2;
2805         nbNodes = 2;
2806         elemType = & segType;
2807
2808         // get nodes for new elements
2809         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2810         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811         break;
2812       }
2813       default: continue;
2814       } // switch( elem->GetEntityType() )
2815
2816       // Create new elements
2817
2818       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819
2820       splitElems.clear();
2821
2822       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2823       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2824       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2825       //elemType->SetID( -1 );
2826
2827       for ( int iE = 0; iE < nbElems; ++iE )
2828         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2829
2830
2831       ReplaceElemInGroups( elem, splitElems, mesh );
2832
2833       if ( subMesh )
2834         for ( size_t i = 0; i < splitElems.size(); ++i )
2835           subMesh->AddElement( splitElems[i] );
2836     }
2837   }
2838 }
2839
2840 //=======================================================================
2841 //function : AddToSameGroups
2842 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2843 //=======================================================================
2844
2845 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2846                                         const SMDS_MeshElement* elemInGroups,
2847                                         SMESHDS_Mesh *          aMesh)
2848 {
2849   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850   if (!groups.empty()) {
2851     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2852     for ( ; grIt != groups.end(); grIt++ ) {
2853       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2854       if ( group && group->Contains( elemInGroups ))
2855         group->SMDSGroup().Add( elemToAdd );
2856     }
2857   }
2858 }
2859
2860
2861 //=======================================================================
2862 //function : RemoveElemFromGroups
2863 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2864 //=======================================================================
2865 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2866                                              SMESHDS_Mesh *          aMesh)
2867 {
2868   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2869   if (!groups.empty())
2870   {
2871     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2872     for (; GrIt != groups.end(); GrIt++)
2873     {
2874       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2875       if (!grp || grp->IsEmpty()) continue;
2876       grp->SMDSGroup().Remove(removeelem);
2877     }
2878   }
2879 }
2880
2881 //================================================================================
2882 /*!
2883  * \brief Replace elemToRm by elemToAdd in the all groups
2884  */
2885 //================================================================================
2886
2887 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2888                                             const SMDS_MeshElement* elemToAdd,
2889                                             SMESHDS_Mesh *          aMesh)
2890 {
2891   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2892   if (!groups.empty()) {
2893     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2894     for ( ; grIt != groups.end(); grIt++ ) {
2895       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2896       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2897         group->SMDSGroup().Add( elemToAdd );
2898     }
2899   }
2900 }
2901
2902 //================================================================================
2903 /*!
2904  * \brief Replace elemToRm by elemToAdd in the all groups
2905  */
2906 //================================================================================
2907
2908 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2909                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2910                                             SMESHDS_Mesh *                         aMesh)
2911 {
2912   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2913   if (!groups.empty())
2914   {
2915     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2916     for ( ; grIt != groups.end(); grIt++ ) {
2917       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2918       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2919         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2920           group->SMDSGroup().Add( elemToAdd[ i ] );
2921     }
2922   }
2923 }
2924
2925 //=======================================================================
2926 //function : QuadToTri
2927 //purpose  : Cut quadrangles into triangles.
2928 //           theCrit is used to select a diagonal to cut
2929 //=======================================================================
2930
2931 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2932                                   const bool         the13Diag)
2933 {
2934   myLastCreatedElems.Clear();
2935   myLastCreatedNodes.Clear();
2936
2937   SMESHDS_Mesh * aMesh = GetMeshDS();
2938
2939   Handle(Geom_Surface) surface;
2940   SMESH_MesherHelper   helper( *GetMesh() );
2941
2942   TIDSortedElemSet::iterator itElem;
2943   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944   {
2945     const SMDS_MeshElement* elem = *itElem;
2946     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2947       continue;
2948
2949     if ( elem->NbNodes() == 4 ) {
2950       // retrieve element nodes
2951       const SMDS_MeshNode* aNodes [4];
2952       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953       int i = 0;
2954       while ( itN->more() )
2955         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956
2957       int aShapeId = FindShape( elem );
2958       const SMDS_MeshElement* newElem1 = 0;
2959       const SMDS_MeshElement* newElem2 = 0;
2960       if ( the13Diag ) {
2961         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2963       }
2964       else {
2965         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967       }
2968       myLastCreatedElems.Append(newElem1);
2969       myLastCreatedElems.Append(newElem2);
2970       // put a new triangle on the same shape and add to the same groups
2971       if ( aShapeId )
2972       {
2973         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975       }
2976       AddToSameGroups( newElem1, elem, aMesh );
2977       AddToSameGroups( newElem2, elem, aMesh );
2978       aMesh->RemoveElement( elem );
2979     }
2980
2981     // Quadratic quadrangle
2982
2983     else if ( elem->NbNodes() >= 8 )
2984     {
2985       // get surface elem is on
2986       int aShapeId = FindShape( elem );
2987       if ( aShapeId != helper.GetSubShapeID() ) {
2988         surface.Nullify();
2989         TopoDS_Shape shape;
2990         if ( aShapeId > 0 )
2991           shape = aMesh->IndexToShape( aShapeId );
2992         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993           TopoDS_Face face = TopoDS::Face( shape );
2994           surface = BRep_Tool::Surface( face );
2995           if ( !surface.IsNull() )
2996             helper.SetSubShape( shape );
2997         }
2998       }
2999
3000       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3001       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3002       for ( int i = 0; itN->more(); ++i )
3003         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004
3005       const SMDS_MeshNode* centrNode = aNodes[8];
3006       if ( centrNode == 0 )
3007       {
3008         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3009                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010                                            surface.IsNull() );
3011         myLastCreatedNodes.Append(centrNode);
3012       }
3013
3014       // create a new element
3015       const SMDS_MeshElement* newElem1 = 0;
3016       const SMDS_MeshElement* newElem2 = 0;
3017       if ( the13Diag ) {
3018         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3019                                   aNodes[6], aNodes[7], centrNode );
3020         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3021                                   centrNode, aNodes[4], aNodes[5] );
3022       }
3023       else {
3024         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3025                                   aNodes[7], aNodes[4], centrNode );
3026         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3027                                   centrNode, aNodes[5], aNodes[6] );
3028       }
3029       myLastCreatedElems.Append(newElem1);
3030       myLastCreatedElems.Append(newElem2);
3031       // put a new triangle on the same shape and add to the same groups
3032       if ( aShapeId )
3033       {
3034         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3035         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036       }
3037       AddToSameGroups( newElem1, elem, aMesh );
3038       AddToSameGroups( newElem2, elem, aMesh );
3039       aMesh->RemoveElement( elem );
3040     }
3041   }
3042
3043   return true;
3044 }
3045
3046 //=======================================================================
3047 //function : getAngle
3048 //purpose  :
3049 //=======================================================================
3050
3051 double getAngle(const SMDS_MeshElement * tr1,
3052                 const SMDS_MeshElement * tr2,
3053                 const SMDS_MeshNode *    n1,
3054                 const SMDS_MeshNode *    n2)
3055 {
3056   double angle = 2. * M_PI; // bad angle
3057
3058   // get normals
3059   SMESH::Controls::TSequenceOfXYZ P1, P2;
3060   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3061        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3062     return angle;
3063   gp_Vec N1,N2;
3064   if(!tr1->IsQuadratic())
3065     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066   else
3067     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3068   if ( N1.SquareMagnitude() <= gp::Resolution() )
3069     return angle;
3070   if(!tr2->IsQuadratic())
3071     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072   else
3073     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3074   if ( N2.SquareMagnitude() <= gp::Resolution() )
3075     return angle;
3076
3077   // find the first diagonal node n1 in the triangles:
3078   // take in account a diagonal link orientation
3079   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3080   for ( int t = 0; t < 2; t++ ) {
3081     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3082     int i = 0, iDiag = -1;
3083     while ( it->more()) {
3084       const SMDS_MeshElement *n = it->next();
3085       if ( n == n1 || n == n2 ) {
3086         if ( iDiag < 0)
3087           iDiag = i;
3088         else {
3089           if ( i - iDiag == 1 )
3090             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3091           else
3092             nFirst[ t ] = n;
3093           break;
3094         }
3095       }
3096       i++;
3097     }
3098   }
3099   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3100     N2.Reverse();
3101
3102   angle = N1.Angle( N2 );
3103   //SCRUTE( angle );
3104   return angle;
3105 }
3106
3107 // =================================================
3108 // class generating a unique ID for a pair of nodes
3109 // and able to return nodes by that ID
3110 // =================================================
3111 class LinkID_Gen {
3112 public:
3113
3114   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3115     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3116   {}
3117
3118   long GetLinkID (const SMDS_MeshNode * n1,
3119                   const SMDS_MeshNode * n2) const
3120   {
3121     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3122   }
3123
3124   bool GetNodes (const long             theLinkID,
3125                  const SMDS_MeshNode* & theNode1,
3126                  const SMDS_MeshNode* & theNode2) const
3127   {
3128     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3129     if ( !theNode1 ) return false;
3130     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3131     if ( !theNode2 ) return false;
3132     return true;
3133   }
3134
3135 private:
3136   LinkID_Gen();
3137   const SMESHDS_Mesh* myMesh;
3138   long                myMaxID;
3139 };
3140
3141
3142 //=======================================================================
3143 //function : TriToQuad
3144 //purpose  : Fuse neighbour triangles into quadrangles.
3145 //           theCrit is used to select a neighbour to fuse with.
3146 //           theMaxAngle is a max angle between element normals at which
3147 //           fusion is still performed.
3148 //=======================================================================
3149
3150 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3151                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3152                                   const double                         theMaxAngle)
3153 {
3154   myLastCreatedElems.Clear();
3155   myLastCreatedNodes.Clear();
3156
3157   if ( !theCrit.get() )
3158     return false;
3159
3160   SMESHDS_Mesh * aMesh = GetMeshDS();
3161
3162   // Prepare data for algo: build
3163   // 1. map of elements with their linkIDs
3164   // 2. map of linkIDs with their elements
3165
3166   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3167   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3168   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3169   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3170
3171   TIDSortedElemSet::iterator itElem;
3172   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3173   {
3174     const SMDS_MeshElement* elem = *itElem;
3175     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3176     bool IsTria = ( elem->NbCornerNodes()==3 );
3177     if (!IsTria) continue;
3178
3179     // retrieve element nodes
3180     const SMDS_MeshNode* aNodes [4];
3181     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3182     int i = 0;
3183     while ( i < 3 )
3184       aNodes[ i++ ] = itN->next();
3185     aNodes[ 3 ] = aNodes[ 0 ];
3186
3187     // fill maps
3188     for ( i = 0; i < 3; i++ ) {
3189       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3190       // check if elements sharing a link can be fused
3191       itLE = mapLi_listEl.find( link );
3192       if ( itLE != mapLi_listEl.end() ) {
3193         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3194           continue;
3195         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3196         //if ( FindShape( elem ) != FindShape( elem2 ))
3197         //  continue; // do not fuse triangles laying on different shapes
3198         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3199           continue; // avoid making badly shaped quads
3200         (*itLE).second.push_back( elem );
3201       }
3202       else {
3203         mapLi_listEl[ link ].push_back( elem );
3204       }
3205       mapEl_setLi [ elem ].insert( link );
3206     }
3207   }
3208   // Clean the maps from the links shared by a sole element, ie
3209   // links to which only one element is bound in mapLi_listEl
3210
3211   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3212     int nbElems = (*itLE).second.size();
3213     if ( nbElems < 2  ) {
3214       const SMDS_MeshElement* elem = (*itLE).second.front();
3215       SMESH_TLink link = (*itLE).first;
3216       mapEl_setLi[ elem ].erase( link );
3217       if ( mapEl_setLi[ elem ].empty() )
3218         mapEl_setLi.erase( elem );
3219     }
3220   }
3221
3222   // Algo: fuse triangles into quadrangles
3223
3224   while ( ! mapEl_setLi.empty() ) {
3225     // Look for the start element:
3226     // the element having the least nb of shared links
3227     const SMDS_MeshElement* startElem = 0;
3228     int minNbLinks = 4;
3229     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3230       int nbLinks = (*itEL).second.size();
3231       if ( nbLinks < minNbLinks ) {
3232         startElem = (*itEL).first;
3233         minNbLinks = nbLinks;
3234         if ( minNbLinks == 1 )
3235           break;
3236       }
3237     }
3238
3239     // search elements to fuse starting from startElem or links of elements
3240     // fused earlyer - startLinks
3241     list< SMESH_TLink > startLinks;
3242     while ( startElem || !startLinks.empty() ) {
3243       while ( !startElem && !startLinks.empty() ) {
3244         // Get an element to start, by a link
3245         SMESH_TLink linkId = startLinks.front();
3246         startLinks.pop_front();
3247         itLE = mapLi_listEl.find( linkId );
3248         if ( itLE != mapLi_listEl.end() ) {
3249           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3250           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3251           for ( ; itE != listElem.end() ; itE++ )
3252             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3253               startElem = (*itE);
3254           mapLi_listEl.erase( itLE );
3255         }
3256       }
3257
3258       if ( startElem ) {
3259         // Get candidates to be fused
3260         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3261         const SMESH_TLink *link12 = 0, *link13 = 0;
3262         startElem = 0;
3263         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3264         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3265         ASSERT( !setLi.empty() );
3266         set< SMESH_TLink >::iterator itLi;
3267         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3268         {
3269           const SMESH_TLink & link = (*itLi);
3270           itLE = mapLi_listEl.find( link );
3271           if ( itLE == mapLi_listEl.end() )
3272             continue;
3273
3274           const SMDS_MeshElement* elem = (*itLE).second.front();
3275           if ( elem == tr1 )
3276             elem = (*itLE).second.back();
3277           mapLi_listEl.erase( itLE );
3278           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3279             continue;
3280           if ( tr2 ) {
3281             tr3 = elem;
3282             link13 = &link;
3283           }
3284           else {
3285             tr2 = elem;
3286             link12 = &link;
3287           }
3288
3289           // add other links of elem to list of links to re-start from
3290           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3291           set< SMESH_TLink >::iterator it;
3292           for ( it = links.begin(); it != links.end(); it++ ) {
3293             const SMESH_TLink& link2 = (*it);
3294             if ( link2 != link )
3295               startLinks.push_back( link2 );
3296           }
3297         }
3298
3299         // Get nodes of possible quadrangles
3300         const SMDS_MeshNode *n12 [4], *n13 [4];
3301         bool Ok12 = false, Ok13 = false;
3302         const SMDS_MeshNode *linkNode1, *linkNode2;
3303         if(tr2) {
3304           linkNode1 = link12->first;
3305           linkNode2 = link12->second;
3306           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3307             Ok12 = true;
3308         }
3309         if(tr3) {
3310           linkNode1 = link13->first;
3311           linkNode2 = link13->second;
3312           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3313             Ok13 = true;
3314         }
3315
3316         // Choose a pair to fuse
3317         if ( Ok12 && Ok13 ) {
3318           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3319           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3320           double aBadRate12 = getBadRate( &quad12, theCrit );
3321           double aBadRate13 = getBadRate( &quad13, theCrit );
3322           if (  aBadRate13 < aBadRate12 )
3323             Ok12 = false;
3324           else
3325             Ok13 = false;
3326         }
3327
3328         // Make quadrangles
3329         // and remove fused elems and remove links from the maps
3330         mapEl_setLi.erase( tr1 );
3331         if ( Ok12 )
3332         {
3333           mapEl_setLi.erase( tr2 );
3334           mapLi_listEl.erase( *link12 );
3335           if ( tr1->NbNodes() == 3 )
3336           {
3337             const SMDS_MeshElement* newElem = 0;
3338             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3339             myLastCreatedElems.Append(newElem);
3340             AddToSameGroups( newElem, tr1, aMesh );
3341             int aShapeId = tr1->getshapeId();
3342             if ( aShapeId )
3343               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3344             aMesh->RemoveElement( tr1 );
3345             aMesh->RemoveElement( tr2 );
3346           }
3347           else {
3348             vector< const SMDS_MeshNode* > N1;
3349             vector< const SMDS_MeshNode* > N2;
3350             getNodesFromTwoTria(tr1,tr2,N1,N2);
3351             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3352             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3353             // i.e. first nodes from both arrays form a new diagonal
3354             const SMDS_MeshNode* aNodes[8];
3355             aNodes[0] = N1[0];
3356             aNodes[1] = N1[1];
3357             aNodes[2] = N2[0];
3358             aNodes[3] = N2[1];
3359             aNodes[4] = N1[3];
3360             aNodes[5] = N2[5];
3361             aNodes[6] = N2[3];
3362             aNodes[7] = N1[5];
3363             const SMDS_MeshElement* newElem = 0;
3364             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3365               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3366                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3367             else
3368               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3370             myLastCreatedElems.Append(newElem);
3371             AddToSameGroups( newElem, tr1, aMesh );
3372             int aShapeId = tr1->getshapeId();
3373             if ( aShapeId )
3374               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3375             aMesh->RemoveElement( tr1 );
3376             aMesh->RemoveElement( tr2 );
3377             // remove middle node (9)
3378             if ( N1[4]->NbInverseElements() == 0 )
3379               aMesh->RemoveNode( N1[4] );
3380             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3381               aMesh->RemoveNode( N1[6] );
3382             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N2[6] );
3384           }
3385         }
3386         else if ( Ok13 )
3387         {
3388           mapEl_setLi.erase( tr3 );
3389           mapLi_listEl.erase( *link13 );
3390           if ( tr1->NbNodes() == 3 ) {
3391             const SMDS_MeshElement* newElem = 0;
3392             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3393             myLastCreatedElems.Append(newElem);
3394             AddToSameGroups( newElem, tr1, aMesh );
3395             int aShapeId = tr1->getshapeId();
3396             if ( aShapeId )
3397               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3398             aMesh->RemoveElement( tr1 );
3399             aMesh->RemoveElement( tr3 );
3400           }
3401           else {
3402             vector< const SMDS_MeshNode* > N1;
3403             vector< const SMDS_MeshNode* > N2;
3404             getNodesFromTwoTria(tr1,tr3,N1,N2);
3405             // now we receive following N1 and N2 (using numeration as above image)
3406             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3407             // i.e. first nodes from both arrays form a new diagonal
3408             const SMDS_MeshNode* aNodes[8];
3409             aNodes[0] = N1[0];
3410             aNodes[1] = N1[1];
3411             aNodes[2] = N2[0];
3412             aNodes[3] = N2[1];
3413             aNodes[4] = N1[3];
3414             aNodes[5] = N2[5];
3415             aNodes[6] = N2[3];
3416             aNodes[7] = N1[5];
3417             const SMDS_MeshElement* newElem = 0;
3418             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3419               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3420                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3421             else
3422               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3424             myLastCreatedElems.Append(newElem);
3425             AddToSameGroups( newElem, tr1, aMesh );
3426             int aShapeId = tr1->getshapeId();
3427             if ( aShapeId )
3428               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3429             aMesh->RemoveElement( tr1 );
3430             aMesh->RemoveElement( tr3 );
3431             // remove middle node (9)
3432             if ( N1[4]->NbInverseElements() == 0 )
3433               aMesh->RemoveNode( N1[4] );
3434             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3435               aMesh->RemoveNode( N1[6] );
3436             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N2[6] );
3438           }
3439         }
3440
3441         // Next element to fuse: the rejected one
3442         if ( tr3 )
3443           startElem = Ok12 ? tr3 : tr2;
3444
3445       } // if ( startElem )
3446     } // while ( startElem || !startLinks.empty() )
3447   } // while ( ! mapEl_setLi.empty() )
3448
3449   return true;
3450 }
3451
3452
3453 /*#define DUMPSO(txt) \
3454 //  cout << txt << endl;
3455 //=============================================================================
3456 //
3457 //
3458 //
3459 //=============================================================================
3460 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3461 {
3462 if ( i1 == i2 )
3463 return;
3464 int tmp = idNodes[ i1 ];
3465 idNodes[ i1 ] = idNodes[ i2 ];
3466 idNodes[ i2 ] = tmp;
3467 gp_Pnt Ptmp = P[ i1 ];
3468 P[ i1 ] = P[ i2 ];
3469 P[ i2 ] = Ptmp;
3470 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3471 }
3472
3473 //=======================================================================
3474 //function : SortQuadNodes
3475 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3476 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3477 //           1 or 2 else 0.
3478 //=======================================================================
3479
3480 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3481 int               idNodes[] )
3482 {
3483   gp_Pnt P[4];
3484   int i;
3485   for ( i = 0; i < 4; i++ ) {
3486     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3487     if ( !n ) return 0;
3488     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3489   }
3490
3491   gp_Vec V1(P[0], P[1]);
3492   gp_Vec V2(P[0], P[2]);
3493   gp_Vec V3(P[0], P[3]);
3494
3495   gp_Vec Cross1 = V1 ^ V2;
3496   gp_Vec Cross2 = V2 ^ V3;
3497
3498   i = 0;
3499   if (Cross1.Dot(Cross2) < 0)
3500   {
3501     Cross1 = V2 ^ V1;
3502     Cross2 = V1 ^ V3;
3503
3504     if (Cross1.Dot(Cross2) < 0)
3505       i = 2;
3506     else
3507       i = 1;
3508     swap ( i, i + 1, idNodes, P );
3509
3510     //     for ( int ii = 0; ii < 4; ii++ ) {
3511     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3512     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3513     //     }
3514   }
3515   return i;
3516 }
3517
3518 //=======================================================================
3519 //function : SortHexaNodes
3520 //purpose  : Set 8 nodes of a hexahedron in a good order.
3521 //           Return success status
3522 //=======================================================================
3523
3524 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3525                                       int               idNodes[] )
3526 {
3527   gp_Pnt P[8];
3528   int i;
3529   DUMPSO( "INPUT: ========================================");
3530   for ( i = 0; i < 8; i++ ) {
3531     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3532     if ( !n ) return false;
3533     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3534     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3535   }
3536   DUMPSO( "========================================");
3537
3538
3539   set<int> faceNodes;  // ids of bottom face nodes, to be found
3540   set<int> checkedId1; // ids of tried 2-nd nodes
3541   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3542   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3543   int iMin, iLoop1 = 0;
3544
3545   // Loop to try the 2-nd nodes
3546
3547   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3548   {
3549     // Find not checked 2-nd node
3550     for ( i = 1; i < 8; i++ )
3551       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3552         int id1 = idNodes[i];
3553         swap ( 1, i, idNodes, P );
3554         checkedId1.insert ( id1 );
3555         break;
3556       }
3557
3558     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3559     // ie that all but meybe one (id3 which is on the same face) nodes
3560     // lay on the same side from the triangle plane.
3561
3562     bool manyInPlane = false; // more than 4 nodes lay in plane
3563     int iLoop2 = 0;
3564     while ( ++iLoop2 < 6 ) {
3565
3566       // get 1-2-3 plane coeffs
3567       Standard_Real A, B, C, D;
3568       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3569       if ( N.SquareMagnitude() > gp::Resolution() )
3570       {
3571         gp_Pln pln ( P[0], N );
3572         pln.Coefficients( A, B, C, D );
3573
3574         // find the node (iMin) closest to pln
3575         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3576         set<int> idInPln;
3577         for ( i = 3; i < 8; i++ ) {
3578           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3579           if ( fabs( dist[i] ) < minDist ) {
3580             minDist = fabs( dist[i] );
3581             iMin = i;
3582           }
3583           if ( fabs( dist[i] ) <= tol )
3584             idInPln.insert( idNodes[i] );
3585         }
3586
3587         // there should not be more than 4 nodes in bottom plane
3588         if ( idInPln.size() > 1 )
3589         {
3590           DUMPSO( "### idInPln.size() = " << idInPln.size());
3591           // idInPlane does not contain the first 3 nodes
3592           if ( manyInPlane || idInPln.size() == 5)
3593             return false; // all nodes in one plane
3594           manyInPlane = true;
3595
3596           // set the 1-st node to be not in plane
3597           for ( i = 3; i < 8; i++ ) {
3598             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3599               DUMPSO( "### Reset 0-th node");
3600               swap( 0, i, idNodes, P );
3601               break;
3602             }
3603           }
3604
3605           // reset to re-check second nodes
3606           leastDist = DBL_MAX;
3607           faceNodes.clear();
3608           checkedId1.clear();
3609           iLoop1 = 0;
3610           break; // from iLoop2;
3611         }
3612
3613         // check that the other 4 nodes are on the same side
3614         bool sameSide = true;
3615         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3616         for ( i = 3; sameSide && i < 8; i++ ) {
3617           if ( i != iMin )
3618             sameSide = ( isNeg == dist[i] <= 0.);
3619         }
3620
3621         // keep best solution
3622         if ( sameSide && minDist < leastDist ) {
3623           leastDist = minDist;
3624           faceNodes.clear();
3625           faceNodes.insert( idNodes[ 1 ] );
3626           faceNodes.insert( idNodes[ 2 ] );
3627           faceNodes.insert( idNodes[ iMin ] );
3628           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3629                   << " leastDist = " << leastDist);
3630           if ( leastDist <= DBL_MIN )
3631             break;
3632         }
3633       }
3634
3635       // set next 3-d node to check
3636       int iNext = 2 + iLoop2;
3637       if ( iNext < 8 ) {
3638         DUMPSO( "Try 2-nd");
3639         swap ( 2, iNext, idNodes, P );
3640       }
3641     } // while ( iLoop2 < 6 )
3642   } // iLoop1
3643
3644   if ( faceNodes.empty() ) return false;
3645
3646   // Put the faceNodes in proper places
3647   for ( i = 4; i < 8; i++ ) {
3648     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3649       // find a place to put
3650       int iTo = 1;
3651       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3652         iTo++;
3653       DUMPSO( "Set faceNodes");
3654       swap ( iTo, i, idNodes, P );
3655     }
3656   }
3657
3658
3659   // Set nodes of the found bottom face in good order
3660   DUMPSO( " Found bottom face: ");
3661   i = SortQuadNodes( theMesh, idNodes );
3662   if ( i ) {
3663     gp_Pnt Ptmp = P[ i ];
3664     P[ i ] = P[ i+1 ];
3665     P[ i+1 ] = Ptmp;
3666   }
3667   //   else
3668   //     for ( int ii = 0; ii < 4; ii++ ) {
3669   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3670   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3671   //    }
3672
3673   // Gravity center of the top and bottom faces
3674   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3675   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3676
3677   // Get direction from the bottom to the top face
3678   gp_Vec upDir ( aGCb, aGCt );
3679   Standard_Real upDirSize = upDir.Magnitude();
3680   if ( upDirSize <= gp::Resolution() ) return false;
3681   upDir / upDirSize;
3682
3683   // Assure that the bottom face normal points up
3684   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3685   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3686   if ( Nb.Dot( upDir ) < 0 ) {
3687     DUMPSO( "Reverse bottom face");
3688     swap( 1, 3, idNodes, P );
3689   }
3690
3691   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3692   Standard_Real minDist = DBL_MAX;
3693   for ( i = 4; i < 8; i++ ) {
3694     // projection of P[i] to the plane defined by P[0] and upDir
3695     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3696     Standard_Real sqDist = P[0].SquareDistance( Pp );
3697     if ( sqDist < minDist ) {
3698       minDist = sqDist;
3699       iMin = i;
3700     }
3701   }
3702   DUMPSO( "Set 4-th");
3703   swap ( 4, iMin, idNodes, P );
3704
3705   // Set nodes of the top face in good order
3706   DUMPSO( "Sort top face");
3707   i = SortQuadNodes( theMesh, &idNodes[4] );
3708   if ( i ) {
3709     i += 4;
3710     gp_Pnt Ptmp = P[ i ];
3711     P[ i ] = P[ i+1 ];
3712     P[ i+1 ] = Ptmp;
3713   }
3714
3715   // Assure that direction of the top face normal is from the bottom face
3716   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3717   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3718   if ( Nt.Dot( upDir ) < 0 ) {
3719     DUMPSO( "Reverse top face");
3720     swap( 5, 7, idNodes, P );
3721   }
3722
3723   //   DUMPSO( "OUTPUT: ========================================");
3724   //   for ( i = 0; i < 8; i++ ) {
3725   //     float *p = ugrid->GetPoint(idNodes[i]);
3726   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3727   //   }
3728
3729   return true;
3730 }*/
3731
3732 //================================================================================
3733 /*!
3734  * \brief Return nodes linked to the given one
3735  * \param theNode - the node
3736  * \param linkedNodes - the found nodes
3737  * \param type - the type of elements to check
3738  *
3739  * Medium nodes are ignored
3740  */
3741 //================================================================================
3742
3743 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3744                                        TIDSortedElemSet &   linkedNodes,
3745                                        SMDSAbs_ElementType  type )
3746 {
3747   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3748   while ( elemIt->more() )
3749   {
3750     const SMDS_MeshElement* elem = elemIt->next();
3751     if(elem->GetType() == SMDSAbs_0DElement)
3752       continue;
3753
3754     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3755     if ( elem->GetType() == SMDSAbs_Volume )
3756     {
3757       SMDS_VolumeTool vol( elem );
3758       while ( nodeIt->more() ) {
3759         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3760         if ( theNode != n && vol.IsLinked( theNode, n ))
3761           linkedNodes.insert( n );
3762       }
3763     }
3764     else
3765     {
3766       for ( int i = 0; nodeIt->more(); ++i ) {
3767         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3768         if ( n == theNode ) {
3769           int iBefore = i - 1;
3770           int iAfter  = i + 1;
3771           if ( elem->IsQuadratic() ) {
3772             int nb = elem->NbNodes() / 2;
3773             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3774             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3775           }
3776           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3777           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3778         }
3779       }
3780     }
3781   }
3782 }
3783
3784 //=======================================================================
3785 //function : laplacianSmooth
3786 //purpose  : pulls theNode toward the center of surrounding nodes directly
3787 //           connected to that node along an element edge
3788 //=======================================================================
3789
3790 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3791                      const Handle(Geom_Surface)&          theSurface,
3792                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3793 {
3794   // find surrounding nodes
3795
3796   TIDSortedElemSet nodeSet;
3797   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3798
3799   // compute new coodrs
3800
3801   double coord[] = { 0., 0., 0. };
3802   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3803   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3804     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3805     if ( theSurface.IsNull() ) { // smooth in 3D
3806       coord[0] += node->X();
3807       coord[1] += node->Y();
3808       coord[2] += node->Z();
3809     }
3810     else { // smooth in 2D
3811       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3812       gp_XY* uv = theUVMap[ node ];
3813       coord[0] += uv->X();
3814       coord[1] += uv->Y();
3815     }
3816   }
3817   int nbNodes = nodeSet.size();
3818   if ( !nbNodes )
3819     return;
3820   coord[0] /= nbNodes;
3821   coord[1] /= nbNodes;
3822
3823   if ( !theSurface.IsNull() ) {
3824     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3825     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3826     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3827     coord[0] = p3d.X();
3828     coord[1] = p3d.Y();
3829     coord[2] = p3d.Z();
3830   }
3831   else
3832     coord[2] /= nbNodes;
3833
3834   // move node
3835
3836   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3837 }
3838
3839 //=======================================================================
3840 //function : centroidalSmooth
3841 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3842 //           surrounding elements
3843 //=======================================================================
3844
3845 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3846                       const Handle(Geom_Surface)&          theSurface,
3847                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3848 {
3849   gp_XYZ aNewXYZ(0.,0.,0.);
3850   SMESH::Controls::Area anAreaFunc;
3851   double totalArea = 0.;
3852   int nbElems = 0;
3853
3854   // compute new XYZ
3855
3856   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3857   while ( elemIt->more() )
3858   {
3859     const SMDS_MeshElement* elem = elemIt->next();
3860     nbElems++;
3861
3862     gp_XYZ elemCenter(0.,0.,0.);
3863     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3864     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3865     int nn = elem->NbNodes();
3866     if(elem->IsQuadratic()) nn = nn/2;
3867     int i=0;
3868     //while ( itN->more() ) {
3869     while ( i<nn ) {
3870       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3871       i++;
3872       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3873       aNodePoints.push_back( aP );
3874       if ( !theSurface.IsNull() ) { // smooth in 2D
3875         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3876         gp_XY* uv = theUVMap[ aNode ];
3877         aP.SetCoord( uv->X(), uv->Y(), 0. );
3878       }
3879       elemCenter += aP;
3880     }
3881     double elemArea = anAreaFunc.GetValue( aNodePoints );
3882     totalArea += elemArea;
3883     elemCenter /= nn;
3884     aNewXYZ += elemCenter * elemArea;
3885   }
3886   aNewXYZ /= totalArea;
3887   if ( !theSurface.IsNull() ) {
3888     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3889     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3890   }
3891
3892   // move node
3893
3894   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3895 }
3896
3897 //=======================================================================
3898 //function : getClosestUV
3899 //purpose  : return UV of closest projection
3900 //=======================================================================
3901
3902 static bool getClosestUV (Extrema_GenExtPS& projector,
3903                           const gp_Pnt&     point,
3904                           gp_XY &           result)
3905 {
3906   projector.Perform( point );
3907   if ( projector.IsDone() ) {
3908     double u, v, minVal = DBL_MAX;
3909     for ( int i = projector.NbExt(); i > 0; i-- )
3910       if ( projector.SquareDistance( i ) < minVal ) {
3911         minVal = projector.SquareDistance( i );
3912         projector.Point( i ).Parameter( u, v );
3913       }
3914     result.SetCoord( u, v );
3915     return true;
3916   }
3917   return false;
3918 }
3919
3920 //=======================================================================
3921 //function : Smooth
3922 //purpose  : Smooth theElements during theNbIterations or until a worst
3923 //           element has aspect ratio <= theTgtAspectRatio.
3924 //           Aspect Ratio varies in range [1.0, inf].
3925 //           If theElements is empty, the whole mesh is smoothed.
3926 //           theFixedNodes contains additionally fixed nodes. Nodes built
3927 //           on edges and boundary nodes are always fixed.
3928 //=======================================================================
3929
3930 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3931                                set<const SMDS_MeshNode*> & theFixedNodes,
3932                                const SmoothMethod          theSmoothMethod,
3933                                const int                   theNbIterations,
3934                                double                      theTgtAspectRatio,
3935                                const bool                  the2D)
3936 {
3937   myLastCreatedElems.Clear();
3938   myLastCreatedNodes.Clear();
3939
3940   if ( theTgtAspectRatio < 1.0 )
3941     theTgtAspectRatio = 1.0;
3942
3943   const double disttol = 1.e-16;
3944
3945   SMESH::Controls::AspectRatio aQualityFunc;
3946
3947   SMESHDS_Mesh* aMesh = GetMeshDS();
3948
3949   if ( theElems.empty() ) {
3950     // add all faces to theElems
3951     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3952     while ( fIt->more() ) {
3953       const SMDS_MeshElement* face = fIt->next();
3954       theElems.insert( theElems.end(), face );
3955     }
3956   }
3957   // get all face ids theElems are on
3958   set< int > faceIdSet;
3959   TIDSortedElemSet::iterator itElem;
3960   if ( the2D )
3961     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3962       int fId = FindShape( *itElem );
3963       // check that corresponding submesh exists and a shape is face
3964       if (fId &&
3965           faceIdSet.find( fId ) == faceIdSet.end() &&
3966           aMesh->MeshElements( fId )) {
3967         TopoDS_Shape F = aMesh->IndexToShape( fId );
3968         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3969           faceIdSet.insert( fId );
3970       }
3971     }
3972   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3973
3974   // ===============================================
3975   // smooth elements on each TopoDS_Face separately
3976   // ===============================================
3977
3978   SMESH_MesherHelper helper( *GetMesh() );
3979
3980   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3981   for ( ; fId != faceIdSet.rend(); ++fId )
3982   {
3983     // get face surface and submesh
3984     Handle(Geom_Surface) surface;
3985     SMESHDS_SubMesh* faceSubMesh = 0;
3986     TopoDS_Face face;
3987     double fToler2 = 0;
3988     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3989     bool isUPeriodic = false, isVPeriodic = false;
3990     if ( *fId )
3991     {
3992       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3993       surface = BRep_Tool::Surface( face );
3994       faceSubMesh = aMesh->MeshElements( *fId );
3995       fToler2 = BRep_Tool::Tolerance( face );
3996       fToler2 *= fToler2 * 10.;
3997       isUPeriodic = surface->IsUPeriodic();
3998       // if ( isUPeriodic )
3999       //   surface->UPeriod();
4000       isVPeriodic = surface->IsVPeriodic();
4001       // if ( isVPeriodic )
4002       //   surface->VPeriod();
4003       surface->Bounds( u1, u2, v1, v2 );
4004       helper.SetSubShape( face );
4005     }
4006     // ---------------------------------------------------------
4007     // for elements on a face, find movable and fixed nodes and
4008     // compute UV for them
4009     // ---------------------------------------------------------
4010     bool checkBoundaryNodes = false;
4011     bool isQuadratic = false;
4012     set<const SMDS_MeshNode*> setMovableNodes;
4013     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4014     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4015     list< const SMDS_MeshElement* > elemsOnFace;
4016
4017     Extrema_GenExtPS projector;
4018     GeomAdaptor_Surface surfAdaptor;
4019     if ( !surface.IsNull() ) {
4020       surfAdaptor.Load( surface );
4021       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4022     }
4023     int nbElemOnFace = 0;
4024     itElem = theElems.begin();
4025     // loop on not yet smoothed elements: look for elems on a face
4026     while ( itElem != theElems.end() )
4027     {
4028       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4029         break; // all elements found
4030
4031       const SMDS_MeshElement* elem = *itElem;
4032       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4033            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4034         ++itElem;
4035         continue;
4036       }
4037       elemsOnFace.push_back( elem );
4038       theElems.erase( itElem++ );
4039       nbElemOnFace++;
4040
4041       if ( !isQuadratic )
4042         isQuadratic = elem->IsQuadratic();
4043
4044       // get movable nodes of elem
4045       const SMDS_MeshNode* node;
4046       SMDS_TypeOfPosition posType;
4047       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4048       int nn = 0, nbn =  elem->NbNodes();
4049       if(elem->IsQuadratic())
4050         nbn = nbn/2;
4051       while ( nn++ < nbn ) {
4052         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4053         const SMDS_PositionPtr& pos = node->GetPosition();
4054         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4055         if (posType != SMDS_TOP_EDGE &&
4056             posType != SMDS_TOP_VERTEX &&
4057             theFixedNodes.find( node ) == theFixedNodes.end())
4058         {
4059           // check if all faces around the node are on faceSubMesh
4060           // because a node on edge may be bound to face
4061           bool all = true;
4062           if ( faceSubMesh ) {
4063             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4064             while ( eIt->more() && all ) {
4065               const SMDS_MeshElement* e = eIt->next();
4066               all = faceSubMesh->Contains( e );
4067             }
4068           }
4069           if ( all )
4070             setMovableNodes.insert( node );
4071           else
4072             checkBoundaryNodes = true;
4073         }
4074         if ( posType == SMDS_TOP_3DSPACE )
4075           checkBoundaryNodes = true;
4076       }
4077
4078       if ( surface.IsNull() )
4079         continue;
4080
4081       // get nodes to check UV
4082       list< const SMDS_MeshNode* > uvCheckNodes;
4083       const SMDS_MeshNode* nodeInFace = 0;
4084       itN = elem->nodesIterator();
4085       nn = 0; nbn =  elem->NbNodes();
4086       if(elem->IsQuadratic())
4087         nbn = nbn/2;
4088       while ( nn++ < nbn ) {
4089         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4090         if ( node->GetPosition()->GetDim() == 2 )
4091           nodeInFace = node;
4092         if ( uvMap.find( node ) == uvMap.end() )
4093           uvCheckNodes.push_back( node );
4094         // add nodes of elems sharing node
4095         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4096         //         while ( eIt->more() ) {
4097         //           const SMDS_MeshElement* e = eIt->next();
4098         //           if ( e != elem ) {
4099         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4100         //             while ( nIt->more() ) {
4101         //               const SMDS_MeshNode* n =
4102         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4103         //               if ( uvMap.find( n ) == uvMap.end() )
4104         //                 uvCheckNodes.push_back( n );
4105         //             }
4106         //           }
4107         //         }
4108       }
4109       // check UV on face
4110       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4111       for ( ; n != uvCheckNodes.end(); ++n ) {
4112         node = *n;
4113         gp_XY uv( 0, 0 );
4114         const SMDS_PositionPtr& pos = node->GetPosition();
4115         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4116         // get existing UV
4117         if ( pos )
4118         {
4119           bool toCheck = true;
4120           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4121         }
4122         // compute not existing UV
4123         bool project = ( posType == SMDS_TOP_3DSPACE );
4124         // double dist1 = DBL_MAX, dist2 = 0;
4125         // if ( posType != SMDS_TOP_3DSPACE ) {
4126         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4127         //   project = dist1 > fToler2;
4128         // }
4129         if ( project ) { // compute new UV
4130           gp_XY newUV;
4131           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4132           if ( !getClosestUV( projector, pNode, newUV )) {
4133             MESSAGE("Node Projection Failed " << node);
4134           }
4135           else {
4136             if ( isUPeriodic )
4137               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4138             if ( isVPeriodic )
4139               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4140             // check new UV
4141             // if ( posType != SMDS_TOP_3DSPACE )
4142             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4143             // if ( dist2 < dist1 )
4144               uv = newUV;
4145           }
4146         }
4147         // store UV in the map
4148         listUV.push_back( uv );
4149         uvMap.insert( make_pair( node, &listUV.back() ));
4150       }
4151     } // loop on not yet smoothed elements
4152
4153     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4154       checkBoundaryNodes = true;
4155
4156     // fix nodes on mesh boundary
4157
4158     if ( checkBoundaryNodes ) {
4159       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4160       map< SMESH_TLink, int >::iterator link_nb;
4161       // put all elements links to linkNbMap
4162       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4163       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4164         const SMDS_MeshElement* elem = (*elemIt);
4165         int nbn =  elem->NbCornerNodes();
4166         // loop on elem links: insert them in linkNbMap
4167         for ( int iN = 0; iN < nbn; ++iN ) {
4168           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4169           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4170           SMESH_TLink link( n1, n2 );
4171           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4172           link_nb->second++;
4173         }
4174       }
4175       // remove nodes that are in links encountered only once from setMovableNodes
4176       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4177         if ( link_nb->second == 1 ) {
4178           setMovableNodes.erase( link_nb->first.node1() );
4179           setMovableNodes.erase( link_nb->first.node2() );
4180         }
4181       }
4182     }
4183
4184     // -----------------------------------------------------
4185     // for nodes on seam edge, compute one more UV ( uvMap2 );
4186     // find movable nodes linked to nodes on seam and which
4187     // are to be smoothed using the second UV ( uvMap2 )
4188     // -----------------------------------------------------
4189
4190     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4191     if ( !surface.IsNull() ) {
4192       TopExp_Explorer eExp( face, TopAbs_EDGE );
4193       for ( ; eExp.More(); eExp.Next() ) {
4194         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4195         if ( !BRep_Tool::IsClosed( edge, face ))
4196           continue;
4197         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4198         if ( !sm ) continue;
4199         // find out which parameter varies for a node on seam
4200         double f,l;
4201         gp_Pnt2d uv1, uv2;
4202         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4203         if ( pcurve.IsNull() ) continue;
4204         uv1 = pcurve->Value( f );
4205         edge.Reverse();
4206         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4207         if ( pcurve.IsNull() ) continue;
4208         uv2 = pcurve->Value( f );
4209         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4210         // assure uv1 < uv2
4211         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4212           std::swap( uv1, uv2 );
4213         // get nodes on seam and its vertices
4214         list< const SMDS_MeshNode* > seamNodes;
4215         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4216         while ( nSeamIt->more() ) {
4217           const SMDS_MeshNode* node = nSeamIt->next();
4218           if ( !isQuadratic || !IsMedium( node ))
4219             seamNodes.push_back( node );
4220         }
4221         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4222         for ( ; vExp.More(); vExp.Next() ) {
4223           sm = aMesh->MeshElements( vExp.Current() );
4224           if ( sm ) {
4225             nSeamIt = sm->GetNodes();
4226             while ( nSeamIt->more() )
4227               seamNodes.push_back( nSeamIt->next() );
4228           }
4229         }
4230         // loop on nodes on seam
4231         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4232         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4233           const SMDS_MeshNode* nSeam = *noSeIt;
4234           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4235           if ( n_uv == uvMap.end() )
4236             continue;
4237           // set the first UV
4238           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4239           // set the second UV
4240           listUV.push_back( *n_uv->second );
4241           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4242           if ( uvMap2.empty() )
4243             uvMap2 = uvMap; // copy the uvMap contents
4244           uvMap2[ nSeam ] = &listUV.back();
4245
4246           // collect movable nodes linked to ones on seam in nodesNearSeam
4247           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4248           while ( eIt->more() ) {
4249             const SMDS_MeshElement* e = eIt->next();
4250             int nbUseMap1 = 0, nbUseMap2 = 0;
4251             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4252             int nn = 0, nbn =  e->NbNodes();
4253             if(e->IsQuadratic()) nbn = nbn/2;
4254             while ( nn++ < nbn )
4255             {
4256               const SMDS_MeshNode* n =
4257                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4258               if (n == nSeam ||
4259                   setMovableNodes.find( n ) == setMovableNodes.end() )
4260                 continue;
4261               // add only nodes being closer to uv2 than to uv1
4262               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4263               //              0.5 * ( n->Y() + nSeam->Y() ),
4264               //              0.5 * ( n->Z() + nSeam->Z() ));
4265               // gp_XY uv;
4266               // getClosestUV( projector, pMid, uv );
4267               double x = uvMap[ n ]->Coord( iPar );
4268               if ( Abs( uv1.Coord( iPar ) - x ) >
4269                    Abs( uv2.Coord( iPar ) - x )) {
4270                 nodesNearSeam.insert( n );
4271                 nbUseMap2++;
4272               }
4273               else
4274                 nbUseMap1++;
4275             }
4276             // for centroidalSmooth all element nodes must
4277             // be on one side of a seam
4278             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4279               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4280               nn = 0;
4281               while ( nn++ < nbn ) {
4282                 const SMDS_MeshNode* n =
4283                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4284                 setMovableNodes.erase( n );
4285               }
4286             }
4287           }
4288         } // loop on nodes on seam
4289       } // loop on edge of a face
4290     } // if ( !face.IsNull() )
4291
4292     if ( setMovableNodes.empty() ) {
4293       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4294       continue; // goto next face
4295     }
4296
4297     // -------------
4298     // SMOOTHING //
4299     // -------------
4300
4301     int it = -1;
4302     double maxRatio = -1., maxDisplacement = -1.;
4303     set<const SMDS_MeshNode*>::iterator nodeToMove;
4304     for ( it = 0; it < theNbIterations; it++ ) {
4305       maxDisplacement = 0.;
4306       nodeToMove = setMovableNodes.begin();
4307       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4308         const SMDS_MeshNode* node = (*nodeToMove);
4309         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4310
4311         // smooth
4312         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4313         if ( theSmoothMethod == LAPLACIAN )
4314           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4315         else
4316           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317
4318         // node displacement
4319         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4320         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4321         if ( aDispl > maxDisplacement )
4322           maxDisplacement = aDispl;
4323       }
4324       // no node movement => exit
4325       //if ( maxDisplacement < 1.e-16 ) {
4326       if ( maxDisplacement < disttol ) {
4327         MESSAGE("-- no node movement --");
4328         break;
4329       }
4330
4331       // check elements quality
4332       maxRatio  = 0;
4333       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4334       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4335         const SMDS_MeshElement* elem = (*elemIt);
4336         if ( !elem || elem->GetType() != SMDSAbs_Face )
4337           continue;
4338         SMESH::Controls::TSequenceOfXYZ aPoints;
4339         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4340           double aValue = aQualityFunc.GetValue( aPoints );
4341           if ( aValue > maxRatio )
4342             maxRatio = aValue;
4343         }
4344       }
4345       if ( maxRatio <= theTgtAspectRatio ) {
4346         //MESSAGE("-- quality achieved --");
4347         break;
4348       }
4349       if (it+1 == theNbIterations) {
4350         //MESSAGE("-- Iteration limit exceeded --");
4351       }
4352     } // smoothing iterations
4353
4354     // MESSAGE(" Face id: " << *fId <<
4355     //         " Nb iterstions: " << it <<
4356     //         " Displacement: " << maxDisplacement <<
4357     //         " Aspect Ratio " << maxRatio);
4358
4359     // ---------------------------------------
4360     // new nodes positions are computed,
4361     // record movement in DS and set new UV
4362     // ---------------------------------------
4363     nodeToMove = setMovableNodes.begin();
4364     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4365       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4366       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4367       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4368       if ( node_uv != uvMap.end() ) {
4369         gp_XY* uv = node_uv->second;
4370         node->SetPosition
4371           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4372       }
4373     }
4374
4375     // move medium nodes of quadratic elements
4376     if ( isQuadratic )
4377     {
4378       vector<const SMDS_MeshNode*> nodes;
4379       bool checkUV;
4380       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4381       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4382       {
4383         const SMDS_MeshElement* QF = *elemIt;
4384         if ( QF->IsQuadratic() )
4385         {
4386           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4387                         SMDS_MeshElement::iterator() );
4388           nodes.push_back( nodes[0] );
4389           gp_Pnt xyz;
4390           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4391           {
4392             if ( !surface.IsNull() )
4393             {
4394               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4395               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4396               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4397               xyz = surface->Value( uv.X(), uv.Y() );
4398             }
4399             else {
4400               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4401             }
4402             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4403               // we have to move a medium node
4404               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4405           }
4406         }
4407       }
4408     }
4409
4410   } // loop on face ids
4411
4412 }
4413
4414 namespace
4415 {
4416   //=======================================================================
4417   //function : isReverse
4418   //purpose  : Return true if normal of prevNodes is not co-directied with
4419   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4420   //           iNotSame is where prevNodes and nextNodes are different.
4421   //           If result is true then future volume orientation is OK
4422   //=======================================================================
4423
4424   bool isReverse(const SMDS_MeshElement*             face,
4425                  const vector<const SMDS_MeshNode*>& prevNodes,
4426                  const vector<const SMDS_MeshNode*>& nextNodes,
4427                  const int                           iNotSame)
4428   {
4429
4430     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4431     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4432     gp_XYZ extrDir( pN - pP ), faceNorm;
4433     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4434
4435     return faceNorm * extrDir < 0.0;
4436   }
4437
4438   //================================================================================
4439   /*!
4440    * \brief Assure that theElemSets[0] holds elements, not nodes
4441    */
4442   //================================================================================
4443
4444   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4445   {
4446     if ( !theElemSets[0].empty() &&
4447          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4448     {
4449       std::swap( theElemSets[0], theElemSets[1] );
4450     }
4451     else if ( !theElemSets[1].empty() &&
4452               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4453     {
4454       std::swap( theElemSets[0], theElemSets[1] );
4455     }
4456   }
4457 }
4458
4459 //=======================================================================
4460 /*!
4461  * \brief Create elements by sweeping an element
4462  * \param elem - element to sweep
4463  * \param newNodesItVec - nodes generated from each node of the element
4464  * \param newElems - generated elements
4465  * \param nbSteps - number of sweeping steps
4466  * \param srcElements - to append elem for each generated element
4467  */
4468 //=======================================================================
4469
4470 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4471                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4472                                     list<const SMDS_MeshElement*>&        newElems,
4473                                     const size_t                          nbSteps,
4474                                     SMESH_SequenceOfElemPtr&              srcElements)
4475 {
4476   SMESHDS_Mesh* aMesh = GetMeshDS();
4477
4478   const int           nbNodes = elem->NbNodes();
4479   const int         nbCorners = elem->NbCornerNodes();
4480   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4481                                                           polyhedron creation !!! */
4482   // Loop on elem nodes:
4483   // find new nodes and detect same nodes indices
4484   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4485   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4486   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4487   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4488
4489   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4490   vector<int> sames(nbNodes);
4491   vector<bool> isSingleNode(nbNodes);
4492
4493   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4494     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4495     const SMDS_MeshNode*                         node = nnIt->first;
4496     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4497     if ( listNewNodes.empty() )
4498       return;
4499
4500     itNN   [ iNode ] = listNewNodes.begin();
4501     prevNod[ iNode ] = node;
4502     nextNod[ iNode ] = listNewNodes.front();
4503
4504     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4505                                                              corner node of linear */
4506     if ( prevNod[ iNode ] != nextNod [ iNode ])
4507       nbDouble += !isSingleNode[iNode];
4508
4509     if( iNode < nbCorners ) { // check corners only
4510       if ( prevNod[ iNode ] == nextNod [ iNode ])
4511         sames[nbSame++] = iNode;
4512       else
4513         iNotSameNode = iNode;
4514     }
4515   }
4516
4517   if ( nbSame == nbNodes || nbSame > 2) {
4518     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4519     return;
4520   }
4521
4522   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4523   {
4524     // fix nodes order to have bottom normal external
4525     if ( baseType == SMDSEntity_Polygon )
4526     {
4527       std::reverse( itNN.begin(), itNN.end() );
4528       std::reverse( prevNod.begin(), prevNod.end() );
4529       std::reverse( midlNod.begin(), midlNod.end() );
4530       std::reverse( nextNod.begin(), nextNod.end() );
4531       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4532     }
4533     else
4534     {
4535       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4536       SMDS_MeshCell::applyInterlace( ind, itNN );
4537       SMDS_MeshCell::applyInterlace( ind, prevNod );
4538       SMDS_MeshCell::applyInterlace( ind, nextNod );
4539       SMDS_MeshCell::applyInterlace( ind, midlNod );
4540       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4541       if ( nbSame > 0 )
4542       {
4543         sames[nbSame] = iNotSameNode;
4544         for ( int j = 0; j <= nbSame; ++j )
4545           for ( size_t i = 0; i < ind.size(); ++i )
4546             if ( ind[i] == sames[j] )
4547             {
4548               sames[j] = i;
4549               break;
4550             }
4551         iNotSameNode = sames[nbSame];
4552       }
4553     }
4554   }
4555   else if ( elem->GetType() == SMDSAbs_Edge )
4556   {
4557     // orient a new face same as adjacent one
4558     int i1, i2;
4559     const SMDS_MeshElement* e;
4560     TIDSortedElemSet dummy;
4561     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4562         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4563         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4564     {
4565       // there is an adjacent face, check order of nodes in it
4566       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4567       if ( sameOrder )
4568       {
4569         std::swap( itNN[0],    itNN[1] );
4570         std::swap( prevNod[0], prevNod[1] );
4571         std::swap( nextNod[0], nextNod[1] );
4572 #if defined(__APPLE__)
4573         std::swap( isSingleNode[0], isSingleNode[1] );
4574 #else
4575         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4576 #endif
4577         if ( nbSame > 0 )
4578           sames[0] = 1 - sames[0];
4579         iNotSameNode = 1 - iNotSameNode;
4580       }
4581     }
4582   }
4583
4584   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4585   if ( nbSame > 0 ) {
4586     iSameNode    = sames[ nbSame-1 ];
4587     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4589     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4590   }
4591
4592   if ( baseType == SMDSEntity_Polygon )
4593   {
4594     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4596   }
4597   else if ( baseType == SMDSEntity_Quad_Polygon )
4598   {
4599     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4601   }
4602
4603   // make new elements
4604   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4605   {
4606     // get next nodes
4607     for ( iNode = 0; iNode < nbNodes; iNode++ )
4608     {
4609       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610       nextNod[ iNode ] = *itNN[ iNode ]++;
4611     }
4612
4613     SMDS_MeshElement* aNewElem = 0;
4614     /*if(!elem->IsPoly())*/ {
4615       switch ( baseType ) {
4616       case SMDSEntity_0D:
4617       case SMDSEntity_Node: { // sweep NODE
4618         if ( nbSame == 0 ) {
4619           if ( isSingleNode[0] )
4620             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4621           else
4622             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4623         }
4624         else
4625           return;
4626         break;
4627       }
4628       case SMDSEntity_Edge: { // sweep EDGE
4629         if ( nbDouble == 0 )
4630         {
4631           if ( nbSame == 0 ) // ---> quadrangle
4632             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633                                       nextNod[ 1 ], nextNod[ 0 ] );
4634           else               // ---> triangle
4635             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636                                       nextNod[ iNotSameNode ] );
4637         }
4638         else                 // ---> polygon
4639         {
4640           vector<const SMDS_MeshNode*> poly_nodes;
4641           poly_nodes.push_back( prevNod[0] );
4642           poly_nodes.push_back( prevNod[1] );
4643           if ( prevNod[1] != nextNod[1] )
4644           {
4645             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646             poly_nodes.push_back( nextNod[1] );
4647           }
4648           if ( prevNod[0] != nextNod[0] )
4649           {
4650             poly_nodes.push_back( nextNod[0] );
4651             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4652           }
4653           switch ( poly_nodes.size() ) {
4654           case 3:
4655             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4656             break;
4657           case 4:
4658             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4660             break;
4661           default:
4662             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4663           }
4664         }
4665         break;
4666       }
4667       case SMDSEntity_Triangle: // TRIANGLE --->
4668         {
4669           if ( nbDouble > 0 ) break;
4670           if ( nbSame == 0 )       // ---> pentahedron
4671             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4673
4674           else if ( nbSame == 1 )  // ---> pyramid
4675             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4677                                          nextNod[ iSameNode ]);
4678
4679           else // 2 same nodes:       ---> tetrahedron
4680             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681                                          nextNod[ iNotSameNode ]);
4682           break;
4683         }
4684       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4685         {
4686           if ( nbSame == 2 )
4687             return;
4688           if ( nbDouble+nbSame == 2 )
4689           {
4690             if(nbSame==0) {      // ---> quadratic quadrangle
4691               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4693             }
4694             else { //(nbSame==1) // ---> quadratic triangle
4695               if(sames[0]==2) {
4696                 return; // medium node on axis
4697               }
4698               else if(sames[0]==0)
4699                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700                                           prevNod[2], midlNod[1], nextNod[2] );
4701               else // sames[0]==1
4702                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703                                           prevNod[2], nextNod[2], midlNod[0]);
4704             }
4705           }
4706           else if ( nbDouble == 3 )
4707           {
4708             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4709               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4711             }
4712           }
4713           else
4714             return;
4715           break;
4716         }
4717       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718         if ( nbDouble > 0 ) break;
4719
4720         if ( nbSame == 0 )       // ---> hexahedron
4721           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4723
4724         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4727                                        nextNod[ iSameNode ]);
4728           newElems.push_back( aNewElem );
4729           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4730                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4732         }
4733         else if ( nbSame == 2 ) { // ---> pentahedron
4734           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735             // iBeforeSame is same too
4736             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4738                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4739           else
4740             // iAfterSame is same too
4741             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4742                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4744         }
4745         break;
4746       }
4747       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4748       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749         if ( nbDouble+nbSame != 3 ) break;
4750         if(nbSame==0) {
4751           // --->  pentahedron with 15 nodes
4752           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753                                        nextNod[0], nextNod[1], nextNod[2],
4754                                        prevNod[3], prevNod[4], prevNod[5],
4755                                        nextNod[3], nextNod[4], nextNod[5],
4756                                        midlNod[0], midlNod[1], midlNod[2]);
4757         }
4758         else if(nbSame==1) {
4759           // --->  2d order pyramid of 13 nodes
4760           int apex = iSameNode;
4761           int i0 = ( apex + 1 ) % nbCorners;
4762           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4763           int i0a = apex + 3;
4764           int i1a = i1 + 3;
4765           int i01 = i0 + 3;
4766           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767                                       nextNod[i0], nextNod[i1], prevNod[apex],
4768                                       prevNod[i01], midlNod[i0],
4769                                       nextNod[i01], midlNod[i1],
4770                                       prevNod[i1a], prevNod[i0a],
4771                                       nextNod[i0a], nextNod[i1a]);
4772         }
4773         else if(nbSame==2) {
4774           // --->  2d order tetrahedron of 10 nodes
4775           int n1 = iNotSameNode;
4776           int n2 = ( n1 + 1             ) % nbCorners;
4777           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4778           int n12 = n1 + 3;
4779           int n23 = n2 + 3;
4780           int n31 = n3 + 3;
4781           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782                                        prevNod[n12], prevNod[n23], prevNod[n31],
4783                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4784         }
4785         break;
4786       }
4787       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4788         if( nbSame == 0 ) {
4789           if ( nbDouble != 4 ) break;
4790           // --->  hexahedron with 20 nodes
4791           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4796         }
4797         else if(nbSame==1) {
4798           // ---> pyramid + pentahedron - can not be created since it is needed
4799           // additional middle node at the center of face
4800           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4801           return;
4802         }
4803         else if( nbSame == 2 ) {
4804           if ( nbDouble != 2 ) break;
4805           // --->  2d order Pentahedron with 15 nodes
4806           int n1,n2,n4,n5;
4807           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808             // iBeforeSame is same too
4809             n1 = iBeforeSame;
4810             n2 = iOpposSame;
4811             n4 = iSameNode;
4812             n5 = iAfterSame;
4813           }
4814           else {
4815             // iAfterSame is same too
4816             n1 = iSameNode;
4817             n2 = iBeforeSame;
4818             n4 = iAfterSame;
4819             n5 = iOpposSame;
4820           }
4821           int n12 = n2 + 4;
4822           int n45 = n4 + 4;
4823           int n14 = n1 + 4;
4824           int n25 = n5 + 4;
4825           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826                                        prevNod[n4], prevNod[n5], nextNod[n5],
4827                                        prevNod[n12], midlNod[n2], nextNod[n12],
4828                                        prevNod[n45], midlNod[n5], nextNod[n45],
4829                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4830         }
4831         break;
4832       }
4833       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4834
4835         if( nbSame == 0 && nbDouble == 9 ) {
4836           // --->  tri-quadratic hexahedron with 27 nodes
4837           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842                                        prevNod[8], // bottom center
4843                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844                                        nextNod[8], // top center
4845                                        midlNod[8]);// elem center
4846         }
4847         else
4848         {
4849           return;
4850         }
4851         break;
4852       }
4853       case SMDSEntity_Polygon: { // sweep POLYGON
4854
4855         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856           // --->  hexagonal prism
4857           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858                                        prevNod[3], prevNod[4], prevNod[5],
4859                                        nextNod[0], nextNod[1], nextNod[2],
4860                                        nextNod[3], nextNod[4], nextNod[5]);
4861         }
4862         break;
4863       }
4864       case SMDSEntity_Ball:
4865         return;
4866
4867       default:
4868         break;
4869       } // switch ( baseType )
4870     } // scope
4871
4872     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4873     {
4874       if ( baseType != SMDSEntity_Polygon )
4875       {
4876         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877         SMDS_MeshCell::applyInterlace( ind, prevNod );
4878         SMDS_MeshCell::applyInterlace( ind, nextNod );
4879         SMDS_MeshCell::applyInterlace( ind, midlNod );
4880         SMDS_MeshCell::applyInterlace( ind, itNN );
4881         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4883       }
4884       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885       vector<int> quantities (nbNodes + 2);
4886       polyedre_nodes.clear();
4887       quantities.clear();
4888
4889       // bottom of prism
4890       for (int inode = 0; inode < nbNodes; inode++)
4891         polyedre_nodes.push_back( prevNod[inode] );
4892       quantities.push_back( nbNodes );
4893
4894       // top of prism
4895       polyedre_nodes.push_back( nextNod[0] );
4896       for (int inode = nbNodes; inode-1; --inode )
4897         polyedre_nodes.push_back( nextNod[inode-1] );
4898       quantities.push_back( nbNodes );
4899
4900       // side faces
4901       // 3--6--2
4902       // |     |
4903       // 7     5
4904       // |     |
4905       // 0--4--1
4906       const int iQuad = elem->IsQuadratic();
4907       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4908       {
4909         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910         int inextface = (iface+1+iQuad) % nbNodes;
4911         int imid      = (iface+1) % nbNodes;
4912         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4913         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914         polyedre_nodes.push_back( prevNod[iface] );             // 1
4915         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4916         {
4917           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4919         }
4920         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4921         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4922         {
4923           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4924           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4925         }
4926         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927         if ( nbFaceNodes > 2 )
4928           quantities.push_back( nbFaceNodes );
4929         else // degenerated face
4930           polyedre_nodes.resize( prevNbNodes );
4931       }
4932       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4933
4934     } // try to create a polyherdal prism
4935
4936     if ( aNewElem ) {
4937       newElems.push_back( aNewElem );
4938       myLastCreatedElems.Append(aNewElem);
4939       srcElements.Append( elem );
4940     }
4941
4942     // set new prev nodes
4943     for ( iNode = 0; iNode < nbNodes; iNode++ )
4944       prevNod[ iNode ] = nextNod[ iNode ];
4945
4946   } // loop on steps
4947 }
4948
4949 //=======================================================================
4950 /*!
4951  * \brief Create 1D and 2D elements around swept elements
4952  * \param mapNewNodes - source nodes and ones generated from them
4953  * \param newElemsMap - source elements and ones generated from them
4954  * \param elemNewNodesMap - nodes generated from each node of each element
4955  * \param elemSet - all swept elements
4956  * \param nbSteps - number of sweeping steps
4957  * \param srcElements - to append elem for each generated element
4958  */
4959 //=======================================================================
4960
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4962                                   TTElemOfElemListMap &    newElemsMap,
4963                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4964                                   TIDSortedElemSet&        elemSet,
4965                                   const int                nbSteps,
4966                                   SMESH_SequenceOfElemPtr& srcElements)
4967 {
4968   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969   SMESHDS_Mesh* aMesh = GetMeshDS();
4970
4971   // Find nodes belonging to only one initial element - sweep them into edges.
4972
4973   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974   for ( ; nList != mapNewNodes.end(); nList++ )
4975   {
4976     const SMDS_MeshNode* node =
4977       static_cast<const SMDS_MeshNode*>( nList->first );
4978     if ( newElemsMap.count( node ))
4979       continue; // node was extruded into edge
4980     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981     int nbInitElems = 0;
4982     const SMDS_MeshElement* el = 0;
4983     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984     while ( eIt->more() && nbInitElems < 2 ) {
4985       const SMDS_MeshElement* e = eIt->next();
4986       SMDSAbs_ElementType  type = e->GetType();
4987       if ( type == SMDSAbs_Volume ||
4988            type < highType ||
4989            !elemSet.count(e))
4990         continue;
4991       if ( type > highType ) {
4992         nbInitElems = 0;
4993         highType    = type;
4994       }
4995       el = e;
4996       ++nbInitElems;
4997     }
4998     if ( nbInitElems == 1 ) {
4999       bool NotCreateEdge = el && el->IsMediumNode(node);
5000       if(!NotCreateEdge) {
5001         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002         list<const SMDS_MeshElement*> newEdges;
5003         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5004       }
5005     }
5006   }
5007
5008   // Make a ceiling for each element ie an equal element of last new nodes.
5009   // Find free links of faces - make edges and sweep them into faces.
5010
5011   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5012
5013   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5014   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5016   {
5017     const SMDS_MeshElement* elem = itElem->first;
5018     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5019
5020     if(itElem->second.size()==0) continue;
5021
5022     const bool isQuadratic = elem->IsQuadratic();
5023
5024     if ( elem->GetType() == SMDSAbs_Edge ) {
5025       // create a ceiling edge
5026       if ( !isQuadratic ) {
5027         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028                                vecNewNodes[ 1 ]->second.back())) {
5029           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030                                                    vecNewNodes[ 1 ]->second.back()));
5031           srcElements.Append( elem );
5032         }
5033       }
5034       else {
5035         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036                                vecNewNodes[ 1 ]->second.back(),
5037                                vecNewNodes[ 2 ]->second.back())) {
5038           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039                                                    vecNewNodes[ 1 ]->second.back(),
5040                                                    vecNewNodes[ 2 ]->second.back()));
5041           srcElements.Append( elem );
5042         }
5043       }
5044     }
5045     if ( elem->GetType() != SMDSAbs_Face )
5046       continue;
5047
5048     bool hasFreeLinks = false;
5049
5050     TIDSortedElemSet avoidSet;
5051     avoidSet.insert( elem );
5052
5053     set<const SMDS_MeshNode*> aFaceLastNodes;
5054     int iNode, nbNodes = vecNewNodes.size();
5055     if ( !isQuadratic ) {
5056       // loop on the face nodes
5057       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059         // look for free links of the face
5060         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063         // check if a link n1-n2 is free
5064         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065           hasFreeLinks = true;
5066           // make a new edge and a ceiling for a new edge
5067           const SMDS_MeshElement* edge;
5068           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070             srcElements.Append( myLastCreatedElems.Last() );
5071           }
5072           n1 = vecNewNodes[ iNode ]->second.back();
5073           n2 = vecNewNodes[ iNext ]->second.back();
5074           if ( !aMesh->FindEdge( n1, n2 )) {
5075             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076             srcElements.Append( edge );
5077           }
5078         }
5079       }
5080     }
5081     else { // elem is quadratic face
5082       int nbn = nbNodes/2;
5083       for ( iNode = 0; iNode < nbn; iNode++ ) {
5084         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089         // check if a link is free
5090         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093           hasFreeLinks = true;
5094           // make an edge and a ceiling for a new edge
5095           // find medium node
5096           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098             srcElements.Append( elem );
5099           }
5100           n1 = vecNewNodes[ iNode ]->second.back();
5101           n2 = vecNewNodes[ iNext ]->second.back();
5102           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105             srcElements.Append( elem );
5106           }
5107         }
5108       }
5109       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5111       }
5112     }
5113
5114     // sweep free links into faces
5115
5116     if ( hasFreeLinks ) {
5117       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5119
5120       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5125       }
5126       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5127         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5129       }
5130       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132         std::advance( v, volNb );
5133         // find indices of free faces of a volume and their source edges
5134         list< int > freeInd;
5135         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137         int iF, nbF = vTool.NbFaces();
5138         for ( iF = 0; iF < nbF; iF ++ ) {
5139           if (vTool.IsFreeFace( iF ) &&
5140               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141               initNodeSet != faceNodeSet) // except an initial face
5142           {
5143             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5144               continue;
5145             if ( faceNodeSet == initNodeSetNoCenter )
5146               continue;
5147             freeInd.push_back( iF );
5148             // find source edge of a free face iF
5149             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150             vector<const SMDS_MeshNode*>::iterator lastCommom;
5151             commonNodes.resize( nbNodes, 0 );
5152             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153                                                 initNodeSet.begin(), initNodeSet.end(),
5154                                                 commonNodes.begin());
5155             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5157             else
5158               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5159 #ifdef _DEBUG_
5160             if ( !srcEdges.back() )
5161             {
5162               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163                    << iF << " of volume #" << vTool.ID() << endl;
5164             }
5165 #endif
5166           }
5167         }
5168         if ( freeInd.empty() )
5169           continue;
5170
5171         // create wall faces for all steps;
5172         // if such a face has been already created by sweep of edge,
5173         // assure that its orientation is OK
5174         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5175         {
5176           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177           vTool.SetExternalNormal();
5178           const int nextShift = vTool.IsForward() ? +1 : -1;
5179           list< int >::iterator ind = freeInd.begin();
5180           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5182           {
5183             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184             int nbn = vTool.NbFaceNodes( *ind );
5185             const SMDS_MeshElement * f = 0;
5186             if ( nbn == 3 )              ///// triangle
5187             {
5188               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5189               if ( !f ||
5190                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5191               {
5192                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5193                                                      nodes[ 1 ],
5194                                                      nodes[ 1 + nextShift ] };
5195                 if ( f )
5196                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5197                 else
5198                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5199                                                             newOrder[ 2 ] ));
5200               }
5201             }
5202             else if ( nbn == 4 )       ///// quadrangle
5203             {
5204               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5205               if ( !f ||
5206                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207               {
5208                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5210                 if ( f )
5211                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212                 else
5213                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214                                                             newOrder[ 2 ], newOrder[ 3 ]));
5215               }
5216             }
5217             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5218             {
5219               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5220               if ( !f ||
5221                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5222               {
5223                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5224                                                      nodes[2],
5225                                                      nodes[2 + 2*nextShift],
5226                                                      nodes[3 - 2*nextShift],
5227                                                      nodes[3],
5228                                                      nodes[3 + 2*nextShift]};
5229                 if ( f )
5230                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231                 else
5232                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5233                                                             newOrder[ 1 ],
5234                                                             newOrder[ 2 ],
5235                                                             newOrder[ 3 ],
5236                                                             newOrder[ 4 ],
5237                                                             newOrder[ 5 ] ));
5238               }
5239             }
5240             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5241             {
5242               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5244               if ( !f ||
5245                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5246               {
5247                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248                                                      nodes[4 - 2*nextShift],
5249                                                      nodes[4],
5250                                                      nodes[4 + 2*nextShift],
5251                                                      nodes[1],
5252                                                      nodes[5 - 2*nextShift],
5253                                                      nodes[5],
5254                                                      nodes[5 + 2*nextShift] };
5255                 if ( f )
5256                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5257                 else
5258                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259                                                            newOrder[ 2 ], newOrder[ 3 ],
5260                                                            newOrder[ 4 ], newOrder[ 5 ],
5261                                                            newOrder[ 6 ], newOrder[ 7 ]));
5262               }
5263             }
5264             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5265             {
5266               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267                                       SMDSAbs_Face, /*noMedium=*/false);
5268               if ( !f ||
5269                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5270               {
5271                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272                                                      nodes[4 - 2*nextShift],
5273                                                      nodes[4],
5274                                                      nodes[4 + 2*nextShift],
5275                                                      nodes[1],
5276                                                      nodes[5 - 2*nextShift],
5277                                                      nodes[5],
5278                                                      nodes[5 + 2*nextShift],
5279                                                      nodes[8] };
5280                 if ( f )
5281                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5282                 else
5283                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284                                                            newOrder[ 2 ], newOrder[ 3 ],
5285                                                            newOrder[ 4 ], newOrder[ 5 ],
5286                                                            newOrder[ 6 ], newOrder[ 7 ],
5287                                                            newOrder[ 8 ]));
5288               }
5289             }
5290             else  //////// polygon
5291             {
5292               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5294               if ( !f ||
5295                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5296               {
5297                 if ( !vTool.IsForward() )
5298                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5299                 if ( f )
5300                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5301                 else
5302                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5303               }
5304             }
5305
5306             while ( srcElements.Length() < myLastCreatedElems.Length() )
5307               srcElements.Append( *srcEdge );
5308
5309           }  // loop on free faces
5310
5311           // go to the next volume
5312           iVol = 0;
5313           while ( iVol++ < nbVolumesByStep ) v++;
5314
5315         } // loop on steps
5316       } // loop on volumes of one step
5317     } // sweep free links into faces
5318
5319     // Make a ceiling face with a normal external to a volume
5320
5321     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5324
5325     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5328     }
5329     if ( iF >= 0 )
5330     {
5331       lastVol.SetExternalNormal();
5332       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333       const               int nbn = lastVol.NbFaceNodes( iF );
5334       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335       if ( !hasFreeLinks ||
5336            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5337       {
5338         const vector<int>& interlace =
5339           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5341
5342         AddElement( nodeVec, anyFace.Init( elem ));
5343
5344         while ( srcElements.Length() < myLastCreatedElems.Length() )
5345           srcElements.Append( elem );
5346       }
5347     }
5348   } // loop on swept elements
5349 }
5350
5351 //=======================================================================
5352 //function : RotationSweep
5353 //purpose  :
5354 //=======================================================================
5355
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5358                                 const gp_Ax1&      theAxis,
5359                                 const double       theAngle,
5360                                 const int          theNbSteps,
5361                                 const double       theTol,
5362                                 const bool         theMakeGroups,
5363                                 const bool         theMakeWalls)
5364 {
5365   myLastCreatedElems.Clear();
5366   myLastCreatedNodes.Clear();
5367
5368   // source elements for each generated one
5369   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5370
5371   gp_Trsf aTrsf;
5372   aTrsf.SetRotation( theAxis, theAngle );
5373   gp_Trsf aTrsf2;
5374   aTrsf2.SetRotation( theAxis, theAngle/2. );
5375
5376   gp_Lin aLine( theAxis );
5377   double aSqTol = theTol * theTol;
5378
5379   SMESHDS_Mesh* aMesh = GetMeshDS();
5380
5381   TNodeOfNodeListMap mapNewNodes;
5382   TElemOfVecOfNnlmiMap mapElemNewNodes;
5383   TTElemOfElemListMap newElemsMap;
5384
5385   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5386                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5387                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5388   // loop on theElemSets
5389   setElemsFirst( theElemSets );
5390   TIDSortedElemSet::iterator itElem;
5391   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5392   {
5393     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5394     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5395       const SMDS_MeshElement* elem = *itElem;
5396       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5397         continue;
5398       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5399       newNodesItVec.reserve( elem->NbNodes() );
5400
5401       // loop on elem nodes
5402       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5403       while ( itN->more() )
5404       {
5405         const SMDS_MeshNode* node = cast2Node( itN->next() );
5406
5407         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5408         double coord[3];
5409         aXYZ.Coord( coord[0], coord[1], coord[2] );
5410         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5411
5412         // check if a node has been already sweeped
5413         TNodeOfNodeListMapItr nIt =
5414           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5415         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5416         if ( listNewNodes.empty() )
5417         {
5418           // check if we are to create medium nodes between corner ones
5419           bool needMediumNodes = false;
5420           if ( isQuadraticMesh )
5421           {
5422             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5423             while (it->more() && !needMediumNodes )
5424             {
5425               const SMDS_MeshElement* invElem = it->next();
5426               if ( invElem != elem && !theElems.count( invElem )) continue;
5427               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5428               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5429                 needMediumNodes = true;
5430             }
5431           }
5432
5433           // make new nodes
5434           const SMDS_MeshNode * newNode = node;
5435           for ( int i = 0; i < theNbSteps; i++ ) {
5436             if ( !isOnAxis ) {
5437               if ( needMediumNodes )  // create a medium node
5438               {
5439                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5440                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5441                 myLastCreatedNodes.Append(newNode);
5442                 srcNodes.Append( node );
5443                 listNewNodes.push_back( newNode );
5444                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5445               }
5446               else {
5447                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5448               }
5449               // create a corner node
5450               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5451               myLastCreatedNodes.Append(newNode);
5452               srcNodes.Append( node );
5453               listNewNodes.push_back( newNode );
5454             }
5455             else {
5456               listNewNodes.push_back( newNode );
5457               // if ( needMediumNodes )
5458               //   listNewNodes.push_back( newNode );
5459             }
5460           }
5461         }
5462         newNodesItVec.push_back( nIt );
5463       }
5464       // make new elements
5465       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5466     }
5467   }
5468
5469   if ( theMakeWalls )
5470     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5471
5472   PGroupIDs newGroupIDs;
5473   if ( theMakeGroups )
5474     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5475
5476   return newGroupIDs;
5477 }
5478
5479 //=======================================================================
5480 //function : ExtrusParam
5481 //purpose  : standard construction
5482 //=======================================================================
5483
5484 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5485                                             const int                theNbSteps,
5486                                             const std::list<double>& theScales,
5487                                             const gp_XYZ*            theBasePoint,
5488                                             const int                theFlags,
5489                                             const double             theTolerance):
5490   myDir( theStep ),
5491   myBaseP( Precision::Infinite(), 0, 0 ),
5492   myFlags( theFlags ),
5493   myTolerance( theTolerance ),
5494   myElemsToUse( NULL )
5495 {
5496   mySteps = new TColStd_HSequenceOfReal;
5497   const double stepSize = theStep.Magnitude();
5498   for (int i=1; i<=theNbSteps; i++ )
5499     mySteps->Append( stepSize );
5500
5501   int nbScales = theScales.size();
5502   if ( nbScales > 0 )
5503   {
5504     if ( IsLinearVariation() && nbScales < theNbSteps )
5505     {
5506       myScales.reserve( theNbSteps );
5507       std::list<double>::const_iterator scale = theScales.begin();
5508       double prevScale = 1.0;
5509       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5510       {
5511         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5512         int    stDelta = Max( 1, iStep - myScales.size());
5513         double scDelta = ( *scale - prevScale ) / stDelta;
5514         for ( int iStep = 0; iStep < stDelta; ++iStep )
5515         {
5516           myScales.push_back( prevScale + scDelta );
5517           prevScale = myScales.back();
5518         }
5519         prevScale = *scale;
5520       }
5521     }
5522     else
5523     {
5524       myScales.assign( theScales.begin(), theScales.end() );
5525     }
5526   }
5527   if ( theBasePoint )
5528   {
5529     myBaseP = *theBasePoint;
5530   }
5531
5532   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5533       ( theTolerance > 0 ))
5534   {
5535     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5536   }
5537   else
5538   {
5539     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5540   }
5541 }
5542
5543 //=======================================================================
5544 //function : ExtrusParam
5545 //purpose  : steps are given explicitly
5546 //=======================================================================
5547
5548 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5549                                             Handle(TColStd_HSequenceOfReal) theSteps,
5550                                             const int                       theFlags,
5551                                             const double                    theTolerance):
5552   myDir( theDir ),
5553   mySteps( theSteps ),
5554   myFlags( theFlags ),
5555   myTolerance( theTolerance ),
5556   myElemsToUse( NULL )
5557 {
5558   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5559       ( theTolerance > 0 ))
5560   {
5561     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5562   }
5563   else
5564   {
5565     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5566   }
5567 }
5568
5569 //=======================================================================
5570 //function : ExtrusParam
5571 //purpose  : for extrusion by normal
5572 //=======================================================================
5573
5574 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5575                                             const int    theNbSteps,
5576                                             const int    theFlags,
5577                                             const int    theDim ):
5578   myDir( 1,0,0 ),
5579   mySteps( new TColStd_HSequenceOfReal ),
5580   myFlags( theFlags ),
5581   myTolerance( 0 ),
5582   myElemsToUse( NULL )
5583 {
5584   for (int i = 0; i < theNbSteps; i++ )
5585     mySteps->Append( theStepSize );
5586
5587   if ( theDim == 1 )
5588   {
5589     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5590   }
5591   else
5592   {
5593     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5594   }
5595 }
5596
5597 //=======================================================================
5598 //function : ExtrusParam::SetElementsToUse
5599 //purpose  : stores elements to use for extrusion by normal, depending on
5600 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5601 //           define myBaseP for scaling
5602 //=======================================================================
5603
5604 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5605                                                       const TIDSortedElemSet& nodes )
5606 {
5607   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5608
5609   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5610   {
5611     myBaseP.SetCoord( 0.,0.,0. );
5612     TIDSortedElemSet newNodes;
5613
5614     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5615     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5616     {
5617       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5618       TIDSortedElemSet::const_iterator itElem = elements.begin();
5619       for ( ; itElem != elements.end(); itElem++ )
5620       {
5621         const SMDS_MeshElement* elem = *itElem;
5622         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5623         while ( itN->more() ) {
5624           const SMDS_MeshElement* node = itN->next();
5625           if ( newNodes.insert( node ).second )
5626             myBaseP += SMESH_TNodeXYZ( node );
5627         }
5628       }
5629     }
5630     myBaseP /= newNodes.size();
5631   }
5632 }
5633
5634 //=======================================================================
5635 //function : ExtrusParam::beginStepIter
5636 //purpose  : prepare iteration on steps
5637 //=======================================================================
5638
5639 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5640 {
5641   myWithMediumNodes = withMediumNodes;
5642   myNextStep = 1;
5643   myCurSteps.clear();
5644 }
5645 //=======================================================================
5646 //function : ExtrusParam::moreSteps
5647 //purpose  : are there more steps?
5648 //=======================================================================
5649
5650 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5651 {
5652   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5653 }
5654 //=======================================================================
5655 //function : ExtrusParam::nextStep
5656 //purpose  : returns the next step
5657 //=======================================================================
5658
5659 double SMESH_MeshEditor::ExtrusParam::nextStep()
5660 {
5661   double res = 0;
5662   if ( !myCurSteps.empty() )
5663   {
5664     res = myCurSteps.back();
5665     myCurSteps.pop_back();
5666   }
5667   else if ( myNextStep <= mySteps->Length() )
5668   {
5669     myCurSteps.push_back( mySteps->Value( myNextStep ));
5670     ++myNextStep;
5671     if ( myWithMediumNodes )
5672     {
5673       myCurSteps.back() /= 2.;
5674       myCurSteps.push_back( myCurSteps.back() );
5675     }
5676     res = nextStep();
5677   }
5678   return res;
5679 }
5680
5681 //=======================================================================
5682 //function : ExtrusParam::makeNodesByDir
5683 //purpose  : create nodes for standard extrusion
5684 //=======================================================================
5685
5686 int SMESH_MeshEditor::ExtrusParam::
5687 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5688                 const SMDS_MeshNode*              srcNode,
5689                 std::list<const SMDS_MeshNode*> & newNodes,
5690                 const bool                        makeMediumNodes)
5691 {
5692   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5693
5694   int nbNodes = 0;
5695   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5696   {
5697     p += myDir.XYZ() * nextStep();
5698     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5699     newNodes.push_back( newNode );
5700   }
5701
5702   if ( !myScales.empty() )
5703   {
5704     if ( makeMediumNodes && myMediumScales.empty() )
5705     {
5706       myMediumScales.resize( myScales.size() );
5707       double prevFactor = 1.;
5708       for ( size_t i = 0; i < myScales.size(); ++i )
5709       {
5710         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5711         prevFactor = myScales[i];
5712       }
5713     }
5714     typedef std::vector<double>::iterator ScaleIt;
5715     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5716
5717     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5718
5719     gp_XYZ center = myBaseP;
5720     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5721     size_t iN  = 0;
5722     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5723     {
5724       center += myDir.XYZ() * nextStep();
5725
5726       iSc += int( makeMediumNodes );
5727       ScaleIt& scale = scales[ iSc % 2 ];
5728       
5729       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5730       xyz = ( *scale * ( xyz - center )) + center;
5731       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5732
5733       ++scale;
5734     }
5735   }
5736   return nbNodes;
5737 }
5738
5739 //=======================================================================
5740 //function : ExtrusParam::makeNodesByDirAndSew
5741 //purpose  : create nodes for standard extrusion with sewing
5742 //=======================================================================
5743
5744 int SMESH_MeshEditor::ExtrusParam::
5745 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5746                       const SMDS_MeshNode*              srcNode,
5747                       std::list<const SMDS_MeshNode*> & newNodes,
5748                       const bool                        makeMediumNodes)
5749 {
5750   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5751
5752   int nbNodes = 0;
5753   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5754   {
5755     P1 += myDir.XYZ() * nextStep();
5756
5757     // try to search in sequence of existing nodes
5758     // if myNodes.Length()>0 we 'nave to use given sequence
5759     // else - use all nodes of mesh
5760     const SMDS_MeshNode * node = 0;
5761     if ( myNodes.Length() > 0 ) {
5762       int i;
5763       for(i=1; i<=myNodes.Length(); i++) {
5764         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5765         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5766         {
5767           node = myNodes.Value(i);
5768           break;
5769         }
5770       }
5771     }
5772     else {
5773       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5774       while(itn->more()) {
5775         SMESH_TNodeXYZ P2( itn->next() );
5776         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5777         {
5778           node = P2._node;
5779           break;
5780         }
5781       }
5782     }
5783
5784     if ( !node )
5785       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5786
5787     newNodes.push_back( node );
5788
5789   } // loop on steps
5790
5791   return nbNodes;
5792 }
5793
5794 //=======================================================================
5795 //function : ExtrusParam::makeNodesByNormal2D
5796 //purpose  : create nodes for extrusion using normals of faces
5797 //=======================================================================
5798
5799 int SMESH_MeshEditor::ExtrusParam::
5800 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5801                      const SMDS_MeshNode*              srcNode,
5802                      std::list<const SMDS_MeshNode*> & newNodes,
5803                      const bool                        makeMediumNodes)
5804 {
5805   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5806
5807   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5808
5809   // get normals to faces sharing srcNode
5810   vector< gp_XYZ > norms, baryCenters;
5811   gp_XYZ norm, avgNorm( 0,0,0 );
5812   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5813   while ( faceIt->more() )
5814   {
5815     const SMDS_MeshElement* face = faceIt->next();
5816     if ( myElemsToUse && !myElemsToUse->count( face ))
5817       continue;
5818     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5819     {
5820       norms.push_back( norm );
5821       avgNorm += norm;
5822       if ( !alongAvgNorm )
5823       {
5824         gp_XYZ bc(0,0,0);
5825         int nbN = 0;
5826         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5827           bc += SMESH_TNodeXYZ( nIt->next() );
5828         baryCenters.push_back( bc / nbN );
5829       }
5830     }
5831   }
5832
5833   if ( norms.empty() ) return 0;
5834
5835   double normSize = avgNorm.Modulus();
5836   if ( normSize < std::numeric_limits<double>::min() )
5837     return 0;
5838
5839   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5840   {
5841     myDir = avgNorm;
5842     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5843   }
5844
5845   avgNorm /= normSize;
5846
5847   int nbNodes = 0;
5848   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5849   {
5850     gp_XYZ pNew = p;
5851     double stepSize = nextStep();
5852
5853     if ( norms.size() > 1 )
5854     {
5855       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5856       {
5857         // translate plane of a face
5858         baryCenters[ iF ] += norms[ iF ] * stepSize;
5859
5860         // find point of intersection of the face plane located at baryCenters[ iF ]
5861         // and avgNorm located at pNew
5862         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5863         double dot  = ( norms[ iF ] * avgNorm );
5864         if ( dot < std::numeric_limits<double>::min() )
5865           dot = stepSize * 1e-3;
5866         double step = -( norms[ iF ] * pNew + d ) / dot;
5867         pNew += step * avgNorm;
5868       }
5869     }
5870     else
5871     {
5872       pNew += stepSize * avgNorm;
5873     }
5874     p = pNew;
5875
5876     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5877     newNodes.push_back( newNode );
5878   }
5879   return nbNodes;
5880 }
5881
5882 //=======================================================================
5883 //function : ExtrusParam::makeNodesByNormal1D
5884 //purpose  : create nodes for extrusion using normals of edges
5885 //=======================================================================
5886
5887 int SMESH_MeshEditor::ExtrusParam::
5888 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5889                      const SMDS_MeshNode*              srcNode,
5890                      std::list<const SMDS_MeshNode*> & newNodes,
5891                      const bool                        makeMediumNodes)
5892 {
5893   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5894   return 0;
5895 }
5896
5897 //=======================================================================
5898 //function : ExtrusionSweep
5899 //purpose  :
5900 //=======================================================================
5901
5902 SMESH_MeshEditor::PGroupIDs
5903 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5904                                   const gp_Vec&        theStep,
5905                                   const int            theNbSteps,
5906                                   TTElemOfElemListMap& newElemsMap,
5907                                   const int            theFlags,
5908                                   const double         theTolerance)
5909 {
5910   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5911   return ExtrusionSweep( theElems, aParams, newElemsMap );
5912 }
5913
5914
5915 //=======================================================================
5916 //function : ExtrusionSweep
5917 //purpose  :
5918 //=======================================================================
5919
5920 SMESH_MeshEditor::PGroupIDs
5921 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5922                                   ExtrusParam&         theParams,
5923                                   TTElemOfElemListMap& newElemsMap)
5924 {
5925   myLastCreatedElems.Clear();
5926   myLastCreatedNodes.Clear();
5927
5928   // source elements for each generated one
5929   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5930
5931   setElemsFirst( theElemSets );
5932   const int nbSteps = theParams.NbSteps();
5933   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5934
5935   TNodeOfNodeListMap   mapNewNodes;
5936   TElemOfVecOfNnlmiMap mapElemNewNodes;
5937
5938   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5939                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5940                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5941   // loop on theElems
5942   TIDSortedElemSet::iterator itElem;
5943   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5944   {
5945     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5946     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5947     {
5948       // check element type
5949       const SMDS_MeshElement* elem = *itElem;
5950       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5951         continue;
5952
5953       const size_t nbNodes = elem->NbNodes();
5954       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5955       newNodesItVec.reserve( nbNodes );
5956
5957       // loop on elem nodes
5958       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5959       while ( itN->more() )
5960       {
5961         // check if a node has been already sweeped
5962         const SMDS_MeshNode* node = cast2Node( itN->next() );
5963         TNodeOfNodeListMap::iterator nIt =
5964           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5965         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5966         if ( listNewNodes.empty() )
5967         {
5968           // make new nodes
5969
5970           // check if we are to create medium nodes between corner ones
5971           bool needMediumNodes = false;
5972           if ( isQuadraticMesh )
5973           {
5974             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5975             while (it->more() && !needMediumNodes )
5976             {
5977               const SMDS_MeshElement* invElem = it->next();
5978               if ( invElem != elem && !theElems.count( invElem )) continue;
5979               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5980               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5981                 needMediumNodes = true;
5982             }
5983           }
5984           // create nodes for all steps
5985           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5986           {
5987             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5988             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5989             {
5990               myLastCreatedNodes.Append( *newNodesIt );
5991               srcNodes.Append( node );
5992             }
5993           }
5994           else
5995           {
5996             break; // newNodesItVec will be shorter than nbNodes
5997           }
5998         }
5999         newNodesItVec.push_back( nIt );
6000       }
6001       // make new elements
6002       if ( newNodesItVec.size() == nbNodes )
6003         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6004     }
6005   }
6006
6007   if ( theParams.ToMakeBoundary() ) {
6008     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6009   }
6010   PGroupIDs newGroupIDs;
6011   if ( theParams.ToMakeGroups() )
6012     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6013
6014   return newGroupIDs;
6015 }
6016
6017 //=======================================================================
6018 //function : ExtrusionAlongTrack
6019 //purpose  :
6020 //=======================================================================
6021 SMESH_MeshEditor::Extrusion_Error
6022 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6023                                        SMESH_subMesh*       theTrack,
6024                                        const SMDS_MeshNode* theN1,
6025                                        const bool           theHasAngles,
6026                                        list<double>&        theAngles,
6027                                        const bool           theLinearVariation,
6028                                        const bool           theHasRefPoint,
6029                                        const gp_Pnt&        theRefPoint,
6030                                        const bool           theMakeGroups)
6031 {
6032   myLastCreatedElems.Clear();
6033   myLastCreatedNodes.Clear();
6034
6035   int aNbE;
6036   std::list<double> aPrms;
6037   TIDSortedElemSet::iterator itElem;
6038
6039   gp_XYZ aGC;
6040   TopoDS_Edge aTrackEdge;
6041   TopoDS_Vertex aV1, aV2;
6042
6043   SMDS_ElemIteratorPtr aItE;
6044   SMDS_NodeIteratorPtr aItN;
6045   SMDSAbs_ElementType aTypeE;
6046
6047   TNodeOfNodeListMap mapNewNodes;
6048
6049   // 1. Check data
6050   aNbE = theElements[0].size() + theElements[1].size();
6051   // nothing to do
6052   if ( !aNbE )
6053     return EXTR_NO_ELEMENTS;
6054
6055   // 1.1 Track Pattern
6056   ASSERT( theTrack );
6057
6058   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6059   if ( !pSubMeshDS )
6060     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6061                                 theHasAngles, theAngles, theLinearVariation,
6062                                 theHasRefPoint, theRefPoint, theMakeGroups );
6063
6064   aItE = pSubMeshDS->GetElements();
6065   while ( aItE->more() ) {
6066     const SMDS_MeshElement* pE = aItE->next();
6067     aTypeE = pE->GetType();
6068     // Pattern must contain links only
6069     if ( aTypeE != SMDSAbs_Edge )
6070       return EXTR_PATH_NOT_EDGE;
6071   }
6072
6073   list<SMESH_MeshEditor_PathPoint> fullList;
6074
6075   const TopoDS_Shape& aS = theTrack->GetSubShape();
6076   // Sub-shape for the Pattern must be an Edge or Wire
6077   if( aS.ShapeType() == TopAbs_EDGE ) {
6078     aTrackEdge = TopoDS::Edge( aS );
6079     // the Edge must not be degenerated
6080     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6081       return EXTR_BAD_PATH_SHAPE;
6082     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6083     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6084     const SMDS_MeshNode* aN1 = aItN->next();
6085     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6086     const SMDS_MeshNode* aN2 = aItN->next();
6087     // starting node must be aN1 or aN2
6088     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6089       return EXTR_BAD_STARTING_NODE;
6090     aItN = pSubMeshDS->GetNodes();
6091     while ( aItN->more() ) {
6092       const SMDS_MeshNode* pNode = aItN->next();
6093       const SMDS_EdgePosition* pEPos =
6094         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6095       double aT = pEPos->GetUParameter();
6096       aPrms.push_back( aT );
6097     }
6098     //Extrusion_Error err =
6099     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6100   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6101     list< SMESH_subMesh* > LSM;
6102     TopTools_SequenceOfShape Edges;
6103     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6104     while(itSM->more()) {
6105       SMESH_subMesh* SM = itSM->next();
6106       LSM.push_back(SM);
6107       const TopoDS_Shape& aS = SM->GetSubShape();
6108       Edges.Append(aS);
6109     }
6110     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6111     int startNid = theN1->GetID();
6112     TColStd_MapOfInteger UsedNums;
6113
6114     int NbEdges = Edges.Length();
6115     int i = 1;
6116     for(; i<=NbEdges; i++) {
6117       int k = 0;
6118       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6119       for(; itLSM!=LSM.end(); itLSM++) {
6120         k++;
6121         if(UsedNums.Contains(k)) continue;
6122         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6123         SMESH_subMesh* locTrack = *itLSM;
6124         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6125         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6126         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6127         const SMDS_MeshNode* aN1 = aItN->next();
6128         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6129         const SMDS_MeshNode* aN2 = aItN->next();
6130         // starting node must be aN1 or aN2
6131         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6132         // 2. Collect parameters on the track edge
6133         aPrms.clear();
6134         aItN = locMeshDS->GetNodes();
6135         while ( aItN->more() ) {
6136           const SMDS_MeshNode* pNode = aItN->next();
6137           const SMDS_EdgePosition* pEPos =
6138             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following egde
6148         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6149         else startNid = aN1->GetID();
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6155     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6156     for(; itPP!=firstList.end(); itPP++) {
6157       fullList.push_back( *itPP );
6158     }
6159     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6160     fullList.pop_back();
6161     itLLPP++;
6162     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6163       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6164       itPP = currList.begin();
6165       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6166       gp_Dir D1 = PP1.Tangent();
6167       gp_Dir D2 = PP2.Tangent();
6168       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6169                            (D1.Z()+D2.Z())/2 ) );
6170       PP1.SetTangent(Dnew);
6171       fullList.push_back(PP1);
6172       itPP++;
6173       for(; itPP!=firstList.end(); itPP++) {
6174         fullList.push_back( *itPP );
6175       }
6176       PP1 = fullList.back();
6177       fullList.pop_back();
6178     }
6179     // if wire not closed
6180     fullList.push_back(PP1);
6181     // else ???
6182   }
6183   else {
6184     return EXTR_BAD_PATH_SHAPE;
6185   }
6186
6187   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6188                           theHasRefPoint, theRefPoint, theMakeGroups);
6189 }
6190
6191
6192 //=======================================================================
6193 //function : ExtrusionAlongTrack
6194 //purpose  :
6195 //=======================================================================
6196 SMESH_MeshEditor::Extrusion_Error
6197 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6198                                        SMESH_Mesh*          theTrack,
6199                                        const SMDS_MeshNode* theN1,
6200                                        const bool           theHasAngles,
6201                                        list<double>&        theAngles,
6202                                        const bool           theLinearVariation,
6203                                        const bool           theHasRefPoint,
6204                                        const gp_Pnt&        theRefPoint,
6205                                        const bool           theMakeGroups)
6206 {
6207   myLastCreatedElems.Clear();
6208   myLastCreatedNodes.Clear();
6209
6210   int aNbE;
6211   std::list<double> aPrms;
6212   TIDSortedElemSet::iterator itElem;
6213
6214   gp_XYZ aGC;
6215   TopoDS_Edge aTrackEdge;
6216   TopoDS_Vertex aV1, aV2;
6217
6218   SMDS_ElemIteratorPtr aItE;
6219   SMDS_NodeIteratorPtr aItN;
6220   SMDSAbs_ElementType aTypeE;
6221
6222   TNodeOfNodeListMap mapNewNodes;
6223
6224   // 1. Check data
6225   aNbE = theElements[0].size() + theElements[1].size();
6226   // nothing to do
6227   if ( !aNbE )
6228     return EXTR_NO_ELEMENTS;
6229
6230   // 1.1 Track Pattern
6231   ASSERT( theTrack );
6232
6233   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6234
6235   aItE = pMeshDS->elementsIterator();
6236   while ( aItE->more() ) {
6237     const SMDS_MeshElement* pE = aItE->next();
6238     aTypeE = pE->GetType();
6239     // Pattern must contain links only
6240     if ( aTypeE != SMDSAbs_Edge )
6241       return EXTR_PATH_NOT_EDGE;
6242   }
6243
6244   list<SMESH_MeshEditor_PathPoint> fullList;
6245
6246   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6247
6248   if ( !theTrack->HasShapeToMesh() ) {
6249     //Mesh without shape
6250     const SMDS_MeshNode* currentNode = NULL;
6251     const SMDS_MeshNode* prevNode = theN1;
6252     std::vector<const SMDS_MeshNode*> aNodesList;
6253     aNodesList.push_back(theN1);
6254     int nbEdges = 0, conn=0;
6255     const SMDS_MeshElement* prevElem = NULL;
6256     const SMDS_MeshElement* currentElem = NULL;
6257     int totalNbEdges = theTrack->NbEdges();
6258     SMDS_ElemIteratorPtr nIt;
6259
6260     //check start node
6261     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6262       return EXTR_BAD_STARTING_NODE;
6263     }
6264
6265     conn = nbEdgeConnectivity(theN1);
6266     if( conn != 1 )
6267       return EXTR_PATH_NOT_EDGE;
6268
6269     aItE = theN1->GetInverseElementIterator();
6270     prevElem = aItE->next();
6271     currentElem = prevElem;
6272     //Get all nodes
6273     if(totalNbEdges == 1 ) {
6274       nIt = currentElem->nodesIterator();
6275       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276       if(currentNode == prevNode)
6277         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278       aNodesList.push_back(currentNode);
6279     } else {
6280       nIt = currentElem->nodesIterator();
6281       while( nIt->more() ) {
6282         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283         if(currentNode == prevNode)
6284           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6285         aNodesList.push_back(currentNode);
6286
6287         //case of the closed mesh
6288         if(currentNode == theN1) {
6289           nbEdges++;
6290           break;
6291         }
6292
6293         conn = nbEdgeConnectivity(currentNode);
6294         if(conn > 2) {
6295           return EXTR_PATH_NOT_EDGE;
6296         }else if( conn == 1 && nbEdges > 0 ) {
6297           //End of the path
6298           nbEdges++;
6299           break;
6300         }else {
6301           prevNode = currentNode;
6302           aItE = currentNode->GetInverseElementIterator();
6303           currentElem = aItE->next();
6304           if( currentElem  == prevElem)
6305             currentElem = aItE->next();
6306           nIt = currentElem->nodesIterator();
6307           prevElem = currentElem;
6308           nbEdges++;
6309         }
6310       }
6311     }
6312
6313     if(nbEdges != totalNbEdges)
6314       return EXTR_PATH_NOT_EDGE;
6315
6316     TopTools_SequenceOfShape Edges;
6317     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6318     int startNid = theN1->GetID();
6319     for ( size_t i = 1; i < aNodesList.size(); i++ )
6320     {
6321       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6322       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6323       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6324       list<SMESH_MeshEditor_PathPoint> LPP;
6325       aPrms.clear();
6326       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6327       LLPPs.push_back(LPP);
6328       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6329       else                                        startNid = aNodesList[i-1]->GetID();
6330     }
6331
6332     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6333     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6334     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6335     for(; itPP!=firstList.end(); itPP++) {
6336       fullList.push_back( *itPP );
6337     }
6338
6339     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6340     SMESH_MeshEditor_PathPoint PP2;
6341     fullList.pop_back();
6342     itLLPP++;
6343     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6344       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6345       itPP = currList.begin();
6346       PP2 = currList.front();
6347       gp_Dir D1 = PP1.Tangent();
6348       gp_Dir D2 = PP2.Tangent();
6349       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6350       PP1.SetTangent(Dnew);
6351       fullList.push_back(PP1);
6352       itPP++;
6353       for(; itPP!=currList.end(); itPP++) {
6354         fullList.push_back( *itPP );
6355       }
6356       PP1 = fullList.back();
6357       fullList.pop_back();
6358     }
6359     fullList.push_back(PP1);
6360
6361   } // Sub-shape for the Pattern must be an Edge or Wire
6362   else if ( aS.ShapeType() == TopAbs_EDGE )
6363   {
6364     aTrackEdge = TopoDS::Edge( aS );
6365     // the Edge must not be degenerated
6366     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6367       return EXTR_BAD_PATH_SHAPE;
6368     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6369     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6370     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6371     // starting node must be aN1 or aN2
6372     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6373       return EXTR_BAD_STARTING_NODE;
6374     aItN = pMeshDS->nodesIterator();
6375     while ( aItN->more() ) {
6376       const SMDS_MeshNode* pNode = aItN->next();
6377       if( pNode==aN1 || pNode==aN2 ) continue;
6378       const SMDS_EdgePosition* pEPos =
6379         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6380       double aT = pEPos->GetUParameter();
6381       aPrms.push_back( aT );
6382     }
6383     //Extrusion_Error err =
6384     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6385   }
6386   else if( aS.ShapeType() == TopAbs_WIRE ) {
6387     list< SMESH_subMesh* > LSM;
6388     TopTools_SequenceOfShape Edges;
6389     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6390     for(; eExp.More(); eExp.Next()) {
6391       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6392       if( SMESH_Algo::isDegenerated(E) ) continue;
6393       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6394       if(SM) {
6395         LSM.push_back(SM);
6396         Edges.Append(E);
6397       }
6398     }
6399     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6400     TopoDS_Vertex aVprev;
6401     TColStd_MapOfInteger UsedNums;
6402     int NbEdges = Edges.Length();
6403     int i = 1;
6404     for(; i<=NbEdges; i++) {
6405       int k = 0;
6406       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6407       for(; itLSM!=LSM.end(); itLSM++) {
6408         k++;
6409         if(UsedNums.Contains(k)) continue;
6410         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6411         SMESH_subMesh* locTrack = *itLSM;
6412         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6413         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6414         bool aN1isOK = false, aN2isOK = false;
6415         if ( aVprev.IsNull() ) {
6416           // if previous vertex is not yet defined, it means that we in the beginning of wire
6417           // and we have to find initial vertex corresponding to starting node theN1
6418           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6419           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6420           // starting node must be aN1 or aN2
6421           aN1isOK = ( aN1 && aN1 == theN1 );
6422           aN2isOK = ( aN2 && aN2 == theN1 );
6423         }
6424         else {
6425           // we have specified ending vertex of the previous edge on the previous iteration
6426           // and we have just to check that it corresponds to any vertex in current segment
6427           aN1isOK = aVprev.IsSame( aV1 );
6428           aN2isOK = aVprev.IsSame( aV2 );
6429         }
6430         if ( !aN1isOK && !aN2isOK ) continue;
6431         // 2. Collect parameters on the track edge
6432         aPrms.clear();
6433         aItN = locMeshDS->GetNodes();
6434         while ( aItN->more() ) {
6435           const SMDS_MeshNode*     pNode = aItN->next();
6436           const SMDS_EdgePosition* pEPos =
6437             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6438           double aT = pEPos->GetUParameter();
6439           aPrms.push_back( aT );
6440         }
6441         list<SMESH_MeshEditor_PathPoint> LPP;
6442         //Extrusion_Error err =
6443         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6444         LLPPs.push_back(LPP);
6445         UsedNums.Add(k);
6446         // update startN for search following egde
6447         if ( aN1isOK ) aVprev = aV2;
6448         else           aVprev = aV1;
6449         break;
6450       }
6451     }
6452     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6453     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6454     fullList.splice( fullList.end(), firstList );
6455
6456     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6457     fullList.pop_back();
6458     itLLPP++;
6459     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6460       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6461       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6462       gp_Dir D1 = PP1.Tangent();
6463       gp_Dir D2 = PP2.Tangent();
6464       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6465       PP1.SetTangent(Dnew);
6466       fullList.push_back(PP1);
6467       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6468       PP1 = fullList.back();
6469       fullList.pop_back();
6470     }
6471     // if wire not closed
6472     fullList.push_back(PP1);
6473     // else ???
6474   }
6475   else {
6476     return EXTR_BAD_PATH_SHAPE;
6477   }
6478
6479   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6480                           theHasRefPoint, theRefPoint, theMakeGroups);
6481 }
6482
6483
6484 //=======================================================================
6485 //function : makeEdgePathPoints
6486 //purpose  : auxiliary for ExtrusionAlongTrack
6487 //=======================================================================
6488 SMESH_MeshEditor::Extrusion_Error
6489 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6490                                      const TopoDS_Edge&                aTrackEdge,
6491                                      bool                              FirstIsStart,
6492                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6493 {
6494   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6495   aTolVec=1.e-7;
6496   aTolVec2=aTolVec*aTolVec;
6497   double aT1, aT2;
6498   TopoDS_Vertex aV1, aV2;
6499   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6500   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6501   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6502   // 2. Collect parameters on the track edge
6503   aPrms.push_front( aT1 );
6504   aPrms.push_back( aT2 );
6505   // sort parameters
6506   aPrms.sort();
6507   if( FirstIsStart ) {
6508     if ( aT1 > aT2 ) {
6509       aPrms.reverse();
6510     }
6511   }
6512   else {
6513     if ( aT2 > aT1 ) {
6514       aPrms.reverse();
6515     }
6516   }
6517   // 3. Path Points
6518   SMESH_MeshEditor_PathPoint aPP;
6519   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6520   std::list<double>::iterator aItD = aPrms.begin();
6521   for(; aItD != aPrms.end(); ++aItD) {
6522     double aT = *aItD;
6523     gp_Pnt aP3D;
6524     gp_Vec aVec;
6525     aC3D->D1( aT, aP3D, aVec );
6526     aL2 = aVec.SquareMagnitude();
6527     if ( aL2 < aTolVec2 )
6528       return EXTR_CANT_GET_TANGENT;
6529     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6530     aPP.SetPnt( aP3D );
6531     aPP.SetTangent( aTgt );
6532     aPP.SetParameter( aT );
6533     LPP.push_back(aPP);
6534   }
6535   return EXTR_OK;
6536 }
6537
6538
6539 //=======================================================================
6540 //function : makeExtrElements
6541 //purpose  : auxiliary for ExtrusionAlongTrack
6542 //=======================================================================
6543 SMESH_MeshEditor::Extrusion_Error
6544 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6545                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6546                                    const bool                        theHasAngles,
6547                                    list<double>&                     theAngles,
6548                                    const bool                        theLinearVariation,
6549                                    const bool                        theHasRefPoint,
6550                                    const gp_Pnt&                     theRefPoint,
6551                                    const bool                        theMakeGroups)
6552 {
6553   const int aNbTP = fullList.size();
6554
6555   // Angles
6556   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6557     linearAngleVariation(aNbTP-1, theAngles);
6558
6559   // fill vector of path points with angles
6560   vector<SMESH_MeshEditor_PathPoint> aPPs;
6561   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6562   list<double>::iterator                 itAngles = theAngles.begin();
6563   aPPs.push_back( *itPP++ );
6564   for( ; itPP != fullList.end(); itPP++) {
6565     aPPs.push_back( *itPP );
6566     if ( theHasAngles && itAngles != theAngles.end() )
6567       aPPs.back().SetAngle( *itAngles++ );
6568   }
6569
6570   TNodeOfNodeListMap   mapNewNodes;
6571   TElemOfVecOfNnlmiMap mapElemNewNodes;
6572   TTElemOfElemListMap  newElemsMap;
6573   TIDSortedElemSet::iterator itElem;
6574   // source elements for each generated one
6575   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6576
6577   // 3. Center of rotation aV0
6578   gp_Pnt aV0 = theRefPoint;
6579   if ( !theHasRefPoint )
6580   {
6581     gp_XYZ aGC( 0.,0.,0. );
6582     TIDSortedElemSet newNodes;
6583
6584     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6585     {
6586       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6587       itElem = theElements.begin();
6588       for ( ; itElem != theElements.end(); itElem++ )
6589       {
6590         const SMDS_MeshElement* elem = *itElem;
6591         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6592         while ( itN->more() ) {
6593           const SMDS_MeshElement* node = itN->next();
6594           if ( newNodes.insert( node ).second )
6595             aGC += SMESH_TNodeXYZ( node );
6596         }
6597       }
6598     }
6599     aGC /= newNodes.size();
6600     aV0.SetXYZ( aGC );
6601   } // if (!theHasRefPoint) {
6602
6603   // 4. Processing the elements
6604   SMESHDS_Mesh* aMesh = GetMeshDS();
6605   list<const SMDS_MeshNode*> emptyList;
6606
6607   setElemsFirst( theElemSets );
6608   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6609   {
6610     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6611     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6612     {
6613       const SMDS_MeshElement* elem = *itElem;
6614
6615       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6616       newNodesItVec.reserve( elem->NbNodes() );
6617
6618       // loop on elem nodes
6619       int nodeIndex = -1;
6620       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6621       while ( itN->more() )
6622       {
6623         ++nodeIndex;
6624         // check if a node has been already processed
6625         const SMDS_MeshNode* node = cast2Node( itN->next() );
6626         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6627         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6628         if ( listNewNodes.empty() )
6629         {
6630           // make new nodes
6631           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6632           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6633           gp_Ax1 anAx1, anAxT1T0;
6634           gp_Dir aDT1x, aDT0x, aDT1T0;
6635
6636           aTolAng=1.e-4;
6637
6638           aV0x = aV0;
6639           aPN0 = SMESH_TNodeXYZ( node );
6640
6641           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6642           aP0x = aPP0.Pnt();
6643           aDT0x= aPP0.Tangent();
6644
6645           for ( int j = 1; j < aNbTP; ++j ) {
6646             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6647             aP1x     = aPP1.Pnt();
6648             aDT1x    = aPP1.Tangent();
6649             aAngle1x = aPP1.Angle();
6650
6651             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6652             // Translation
6653             gp_Vec aV01x( aP0x, aP1x );
6654             aTrsf.SetTranslation( aV01x );
6655
6656             // traslated point
6657             aV1x = aV0x.Transformed( aTrsf );
6658             aPN1 = aPN0.Transformed( aTrsf );
6659
6660             // rotation 1 [ T1,T0 ]
6661             aAngleT1T0=-aDT1x.Angle( aDT0x );
6662             if (fabs(aAngleT1T0) > aTolAng)
6663             {
6664               aDT1T0=aDT1x^aDT0x;
6665               anAxT1T0.SetLocation( aV1x );
6666               anAxT1T0.SetDirection( aDT1T0 );
6667               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6668
6669               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6670             }
6671
6672             // rotation 2
6673             if ( theHasAngles ) {
6674               anAx1.SetLocation( aV1x );
6675               anAx1.SetDirection( aDT1x );
6676               aTrsfRot.SetRotation( anAx1, aAngle1x );
6677
6678               aPN1 = aPN1.Transformed( aTrsfRot );
6679             }
6680
6681             // make new node
6682             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6683             {
6684               // create additional node
6685               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6686               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6687               myLastCreatedNodes.Append(newNode);
6688               srcNodes.Append( node );
6689               listNewNodes.push_back( newNode );
6690             }
6691             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6692             myLastCreatedNodes.Append(newNode);
6693             srcNodes.Append( node );
6694             listNewNodes.push_back( newNode );
6695
6696             aPN0 = aPN1;
6697             aP0x = aP1x;
6698             aV0x = aV1x;
6699             aDT0x = aDT1x;
6700           }
6701         }
6702         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6703         {
6704           // if current elem is quadratic and current node is not medium
6705           // we have to check - may be it is needed to insert additional nodes
6706           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6707           if ((int) listNewNodes.size() == aNbTP-1 )
6708           {
6709             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6710             gp_XYZ P(node->X(), node->Y(), node->Z());
6711             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6712             int i;
6713             for(i=0; i<aNbTP-1; i++) {
6714               const SMDS_MeshNode* N = *it;
6715               double x = ( N->X() + P.X() )/2.;
6716               double y = ( N->Y() + P.Y() )/2.;
6717               double z = ( N->Z() + P.Z() )/2.;
6718               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6719               srcNodes.Append( node );
6720               myLastCreatedNodes.Append(newN);
6721               aNodes[2*i] = newN;
6722               aNodes[2*i+1] = N;
6723               P = gp_XYZ(N->X(),N->Y(),N->Z());
6724             }
6725             listNewNodes.clear();
6726             for(i=0; i<2*(aNbTP-1); i++) {
6727               listNewNodes.push_back(aNodes[i]);
6728             }
6729           }
6730         }
6731
6732         newNodesItVec.push_back( nIt );
6733       }
6734
6735       // make new elements
6736       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6737     }
6738   }
6739
6740   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6741
6742   if ( theMakeGroups )
6743     generateGroups( srcNodes, srcElems, "extruded");
6744
6745   return EXTR_OK;
6746 }
6747
6748
6749 //=======================================================================
6750 //function : linearAngleVariation
6751 //purpose  : spread values over nbSteps
6752 //=======================================================================
6753
6754 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6755                                             list<double>& Angles)
6756 {
6757   int nbAngles = Angles.size();
6758   if( nbSteps > nbAngles && nbAngles > 0 )
6759   {
6760     vector<double> theAngles(nbAngles);
6761     theAngles.assign( Angles.begin(), Angles.end() );
6762
6763     list<double> res;
6764     double rAn2St = double( nbAngles ) / double( nbSteps );
6765     double angPrev = 0, angle;
6766     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6767     {
6768       double angCur = rAn2St * ( iSt+1 );
6769       double angCurFloor  = floor( angCur );
6770       double angPrevFloor = floor( angPrev );
6771       if ( angPrevFloor == angCurFloor )
6772         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6773       else {
6774         int iP = int( angPrevFloor );
6775         double angPrevCeil = ceil(angPrev);
6776         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6777
6778         int iC = int( angCurFloor );
6779         if ( iC < nbAngles )
6780           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6781
6782         iP = int( angPrevCeil );
6783         while ( iC-- > iP )
6784           angle += theAngles[ iC ];
6785       }
6786       res.push_back(angle);
6787       angPrev = angCur;
6788     }
6789     Angles.swap( res );
6790   }
6791 }
6792
6793
6794 //================================================================================
6795 /*!
6796  * \brief Move or copy theElements applying theTrsf to their nodes
6797  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6798  *  \param theTrsf - transformation to apply
6799  *  \param theCopy - if true, create translated copies of theElems
6800  *  \param theMakeGroups - if true and theCopy, create translated groups
6801  *  \param theTargetMesh - mesh to copy translated elements into
6802  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6803  */
6804 //================================================================================
6805
6806 SMESH_MeshEditor::PGroupIDs
6807 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6808                              const gp_Trsf&     theTrsf,
6809                              const bool         theCopy,
6810                              const bool         theMakeGroups,
6811                              SMESH_Mesh*        theTargetMesh)
6812 {
6813   myLastCreatedElems.Clear();
6814   myLastCreatedNodes.Clear();
6815
6816   bool needReverse = false;
6817   string groupPostfix;
6818   switch ( theTrsf.Form() ) {
6819   case gp_PntMirror:
6820     needReverse = true;
6821     groupPostfix = "mirrored";
6822     break;
6823   case gp_Ax1Mirror:
6824     groupPostfix = "mirrored";
6825     break;
6826   case gp_Ax2Mirror:
6827     needReverse = true;
6828     groupPostfix = "mirrored";
6829     break;
6830   case gp_Rotation:
6831     groupPostfix = "rotated";
6832     break;
6833   case gp_Translation:
6834     groupPostfix = "translated";
6835     break;
6836   case gp_Scale:
6837     groupPostfix = "scaled";
6838     break;
6839   case gp_CompoundTrsf: // different scale by axis
6840     groupPostfix = "scaled";
6841     break;
6842   default:
6843     needReverse = false;
6844     groupPostfix = "transformed";
6845   }
6846
6847   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6848   SMESHDS_Mesh* aMesh    = GetMeshDS();
6849
6850   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6851   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6852   SMESH_MeshEditor::ElemFeatures elemType;
6853
6854   // map old node to new one
6855   TNodeNodeMap nodeMap;
6856
6857   // elements sharing moved nodes; those of them which have all
6858   // nodes mirrored but are not in theElems are to be reversed
6859   TIDSortedElemSet inverseElemSet;
6860
6861   // source elements for each generated one
6862   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6863
6864   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6865   TIDSortedElemSet orphanNode;
6866
6867   if ( theElems.empty() ) // transform the whole mesh
6868   {
6869     // add all elements
6870     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6871     while ( eIt->more() ) theElems.insert( eIt->next() );
6872     // add orphan nodes
6873     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6874     while ( nIt->more() )
6875     {
6876       const SMDS_MeshNode* node = nIt->next();
6877       if ( node->NbInverseElements() == 0)
6878         orphanNode.insert( node );
6879     }
6880   }
6881
6882   // loop on elements to transform nodes : first orphan nodes then elems
6883   TIDSortedElemSet::iterator itElem;
6884   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6885   for (int i=0; i<2; i++)
6886     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6887     {
6888       const SMDS_MeshElement* elem = *itElem;
6889       if ( !elem )
6890         continue;
6891
6892       // loop on elem nodes
6893       double coord[3];
6894       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6895       while ( itN->more() )
6896       {
6897         const SMDS_MeshNode* node = cast2Node( itN->next() );
6898         // check if a node has been already transformed
6899         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6900           nodeMap.insert( make_pair ( node, node ));
6901         if ( !n2n_isnew.second )
6902           continue;
6903
6904         node->GetXYZ( coord );
6905         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6906         if ( theTargetMesh ) {
6907           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6908           n2n_isnew.first->second = newNode;
6909           myLastCreatedNodes.Append(newNode);
6910           srcNodes.Append( node );
6911         }
6912         else if ( theCopy ) {
6913           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6914           n2n_isnew.first->second = newNode;
6915           myLastCreatedNodes.Append(newNode);
6916           srcNodes.Append( node );
6917         }
6918         else {
6919           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6920           // node position on shape becomes invalid
6921           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6922             ( SMDS_SpacePosition::originSpacePosition() );
6923         }
6924
6925         // keep inverse elements
6926         if ( !theCopy && !theTargetMesh && needReverse ) {
6927           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6928           while ( invElemIt->more() ) {
6929             const SMDS_MeshElement* iel = invElemIt->next();
6930             inverseElemSet.insert( iel );
6931           }
6932         }
6933       }
6934     } // loop on elems in { &orphanNode, &theElems };
6935
6936   // either create new elements or reverse mirrored ones
6937   if ( !theCopy && !needReverse && !theTargetMesh )
6938     return PGroupIDs();
6939
6940   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6941
6942   // Replicate or reverse elements
6943
6944   std::vector<int> iForw;
6945   vector<const SMDS_MeshNode*> nodes;
6946   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6947   {
6948     const SMDS_MeshElement* elem = *itElem;
6949     if ( !elem ) continue;
6950
6951     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6952     size_t               nbNodes  = elem->NbNodes();
6953     if ( geomType == SMDSGeom_NONE ) continue; // node
6954
6955     nodes.resize( nbNodes );
6956
6957     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6958     {
6959       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6960       if (!aPolyedre)
6961         continue;
6962       nodes.clear();
6963       bool allTransformed = true;
6964       int nbFaces = aPolyedre->NbFaces();
6965       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6966       {
6967         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6968         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6969         {
6970           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6971           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6972           if ( nodeMapIt == nodeMap.end() )
6973             allTransformed = false; // not all nodes transformed
6974           else
6975             nodes.push_back((*nodeMapIt).second);
6976         }
6977         if ( needReverse && allTransformed )
6978           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6979       }
6980       if ( !allTransformed )
6981         continue; // not all nodes transformed
6982     }
6983     else // ----------------------- the rest element types
6984     {
6985       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6986       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6987       const vector<int>&    i = needReverse ? iRev : iForw;
6988
6989       // find transformed nodes
6990       size_t iNode = 0;
6991       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6992       while ( itN->more() ) {
6993         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6994         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6995         if ( nodeMapIt == nodeMap.end() )
6996           break; // not all nodes transformed
6997         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6998       }
6999       if ( iNode != nbNodes )
7000         continue; // not all nodes transformed
7001     }
7002
7003     if ( editor ) {
7004       // copy in this or a new mesh
7005       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7006         srcElems.Append( elem );
7007     }
7008     else {
7009       // reverse element as it was reversed by transformation
7010       if ( nbNodes > 2 )
7011         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7012     }
7013
7014   } // loop on elements
7015
7016   if ( editor && editor != this )
7017     myLastCreatedElems = editor->myLastCreatedElems;
7018
7019   PGroupIDs newGroupIDs;
7020
7021   if ( ( theMakeGroups && theCopy ) ||
7022        ( theMakeGroups && theTargetMesh ) )
7023     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7024
7025   return newGroupIDs;
7026 }
7027
7028 //=======================================================================
7029 /*!
7030  * \brief Create groups of elements made during transformation
7031  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7032  *  \param elemGens - elements making corresponding myLastCreatedElems
7033  *  \param postfix - to append to names of new groups
7034  *  \param targetMesh - mesh to create groups in
7035  *  \param topPresent - is there "top" elements that are created by sweeping
7036  */
7037 //=======================================================================
7038
7039 SMESH_MeshEditor::PGroupIDs
7040 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7041                                  const SMESH_SequenceOfElemPtr& elemGens,
7042                                  const std::string&             postfix,
7043                                  SMESH_Mesh*                    targetMesh,
7044                                  const bool                     topPresent)
7045 {
7046   PGroupIDs newGroupIDs( new list<int> );
7047   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7048
7049   // Sort existing groups by types and collect their names
7050
7051   // containers to store an old group and generated new ones;
7052   // 1st new group is for result elems of different type than a source one;
7053   // 2nd new group is for same type result elems ("top" group at extrusion)
7054   using boost::tuple;
7055   using boost::make_tuple;
7056   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7057   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7058   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7059   // group names
7060   set< string > groupNames;
7061
7062   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7063   if ( !groupIt->more() ) return newGroupIDs;
7064
7065   int newGroupID = mesh->GetGroupIds().back()+1;
7066   while ( groupIt->more() )
7067   {
7068     SMESH_Group * group = groupIt->next();
7069     if ( !group ) continue;
7070     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7071     if ( !groupDS || groupDS->IsEmpty() ) continue;
7072     groupNames.insert    ( group->GetName() );
7073     groupDS->SetStoreName( group->GetName() );
7074     const SMDSAbs_ElementType type = groupDS->GetType();
7075     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7077     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7078     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7079   }
7080
7081   // Loop on nodes and elements to add them in new groups
7082
7083   vector< const SMDS_MeshElement* > resultElems;
7084   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7085   {
7086     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7087     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7088     if ( gens.Length() != elems.Length() )
7089       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7090
7091     // loop on created elements
7092     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7093     {
7094       const SMDS_MeshElement* sourceElem = gens( iElem );
7095       if ( !sourceElem ) {
7096         MESSAGE("generateGroups(): NULL source element");
7097         continue;
7098       }
7099       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7100       if ( groupsOldNew.empty() ) { // no groups of this type at all
7101         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7102           ++iElem; // skip all elements made by sourceElem
7103         continue;
7104       }
7105       // collect all elements made by the iElem-th sourceElem
7106       resultElems.clear();
7107       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7108         if ( resElem != sourceElem )
7109           resultElems.push_back( resElem );
7110       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7111         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7112           if ( resElem != sourceElem )
7113             resultElems.push_back( resElem );
7114
7115       const SMDS_MeshElement* topElem = 0;
7116       if ( isNodes ) // there must be a top element
7117       {
7118         topElem = resultElems.back();
7119         resultElems.pop_back();
7120       }
7121       else
7122       {
7123         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7124         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7125           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7126           {
7127             topElem = *resElemIt;
7128             *resElemIt = 0; // erase *resElemIt
7129             break;
7130           }
7131       }
7132       // add resultElems to groups originted from ones the sourceElem belongs to
7133       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7134       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7135       {
7136         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7137         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7138         {
7139           // fill in a new group
7140           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7141           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7142           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7143             if ( *resElemIt )
7144               newGroup.Add( *resElemIt );
7145
7146           // fill a "top" group
7147           if ( topElem )
7148           {
7149             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7150             newTopGroup.Add( topElem );
7151          }
7152         }
7153       }
7154     } // loop on created elements
7155   }// loop on nodes and elements
7156
7157   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7158
7159   list<int> topGrouIds;
7160   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7161   {
7162     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7163     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7164                                       orderedOldNewGroups[i]->get<2>() };
7165     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7166     {
7167       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7168       if ( newGroupDS->IsEmpty() )
7169       {
7170         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7171       }
7172       else
7173       {
7174         // set group type
7175         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7176
7177         // make a name
7178         const bool isTop = ( topPresent &&
7179                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7180                              is2nd );
7181
7182         string name = oldGroupDS->GetStoreName();
7183         { // remove trailing whitespaces (issue 22599)
7184           size_t size = name.size();
7185           while ( size > 1 && isspace( name[ size-1 ]))
7186             --size;
7187           if ( size != name.size() )
7188           {
7189             name.resize( size );
7190             oldGroupDS->SetStoreName( name.c_str() );
7191           }
7192         }
7193         if ( !targetMesh ) {
7194           string suffix = ( isTop ? "top": postfix.c_str() );
7195           name += "_";
7196           name += suffix;
7197           int nb = 1;
7198           while ( !groupNames.insert( name ).second ) // name exists
7199             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7200         }
7201         else if ( isTop ) {
7202           name += "_top";
7203         }
7204         newGroupDS->SetStoreName( name.c_str() );
7205
7206         // make a SMESH_Groups
7207         mesh->AddGroup( newGroupDS );
7208         if ( isTop )
7209           topGrouIds.push_back( newGroupDS->GetID() );
7210         else
7211           newGroupIDs->push_back( newGroupDS->GetID() );
7212       }
7213     }
7214   }
7215   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7216
7217   return newGroupIDs;
7218 }
7219
7220 //================================================================================
7221 /*!
7222  *  * \brief Return list of group of nodes close to each other within theTolerance
7223  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7224  *  *        an Octree algorithm
7225  *  \param [in,out] theNodes - the nodes to treat
7226  *  \param [in]     theTolerance - the tolerance
7227  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7228  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7229  *         corner and medium nodes in separate groups
7230  */
7231 //================================================================================
7232
7233 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7234                                             const double         theTolerance,
7235                                             TListOfListOfNodes & theGroupsOfNodes,
7236                                             bool                 theSeparateCornersAndMedium)
7237 {
7238   myLastCreatedElems.Clear();
7239   myLastCreatedNodes.Clear();
7240
7241   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7242        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7243        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7244     theSeparateCornersAndMedium = false;
7245
7246   TIDSortedNodeSet& corners = theNodes;
7247   TIDSortedNodeSet  medium;
7248
7249   if ( theNodes.empty() ) // get all nodes in the mesh
7250   {
7251     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7252     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7253     if ( theSeparateCornersAndMedium )
7254       while ( nIt->more() )
7255       {
7256         const SMDS_MeshNode* n = nIt->next();
7257         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7258         nodeSet->insert( nodeSet->end(), n );
7259       }
7260     else
7261       while ( nIt->more() )
7262         theNodes.insert( theNodes.end(), nIt->next() );
7263   }
7264   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7265   {
7266     TIDSortedNodeSet::iterator nIt = corners.begin();
7267     while ( nIt != corners.end() )
7268       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7269       {
7270         medium.insert( medium.end(), *nIt );
7271         corners.erase( nIt++ );
7272       }
7273       else
7274       {
7275         ++nIt;
7276       }
7277   }
7278
7279   if ( !corners.empty() )
7280     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7281   if ( !medium.empty() )
7282     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7283 }
7284
7285 //=======================================================================
7286 //function : SimplifyFace
7287 //purpose  : split a chain of nodes into several closed chains
7288 //=======================================================================
7289
7290 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7291                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7292                                     vector<int>&                         quantities) const
7293 {
7294   int nbNodes = faceNodes.size();
7295   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7296     --nbNodes;
7297   if ( nbNodes < 3 )
7298     return 0;
7299   size_t prevNbQuant = quantities.size();
7300
7301   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7302   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7303   map< const SMDS_MeshNode*, int >::iterator nInd;
7304
7305   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7306   simpleNodes.push_back( faceNodes[0] );
7307   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7308   {
7309     if ( faceNodes[ iCur ] != simpleNodes.back() )
7310     {
7311       int index = simpleNodes.size();
7312       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7313       int prevIndex = nInd->second;
7314       if ( prevIndex < index )
7315       {
7316         // a sub-loop found
7317         int loopLen = index - prevIndex;
7318         if ( loopLen > 2 )
7319         {
7320           // store the sub-loop
7321           quantities.push_back( loopLen );
7322           for ( int i = prevIndex; i < index; i++ )
7323             poly_nodes.push_back( simpleNodes[ i ]);
7324         }
7325         simpleNodes.resize( prevIndex+1 );
7326       }
7327       else
7328       {
7329         simpleNodes.push_back( faceNodes[ iCur ]);
7330       }
7331     }
7332   }
7333
7334   if ( simpleNodes.size() > 2 )
7335   {
7336     quantities.push_back( simpleNodes.size() );
7337     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7338   }
7339
7340   return quantities.size() - prevNbQuant;
7341 }
7342
7343 //=======================================================================
7344 //function : MergeNodes
7345 //purpose  : In each group, the cdr of nodes are substituted by the first one
7346 //           in all elements.
7347 //=======================================================================
7348
7349 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7350                                    const bool           theAvoidMakingHoles)
7351 {
7352   myLastCreatedElems.Clear();
7353   myLastCreatedNodes.Clear();
7354
7355   SMESHDS_Mesh* mesh = GetMeshDS();
7356
7357   TNodeNodeMap nodeNodeMap; // node to replace - new node
7358   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7359   list< int > rmElemIds, rmNodeIds;
7360   vector< ElemFeatures > newElemDefs;
7361
7362   // Fill nodeNodeMap and elems
7363
7364   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7365   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7366   {
7367     list<const SMDS_MeshNode*>& nodes = *grIt;
7368     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7369     const SMDS_MeshNode* nToKeep = *nIt;
7370     for ( ++nIt; nIt != nodes.end(); nIt++ )
7371     {
7372       const SMDS_MeshNode* nToRemove = *nIt;
7373       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7374       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7375       while ( invElemIt->more() ) {
7376         const SMDS_MeshElement* elem = invElemIt->next();
7377         elems.insert(elem);
7378       }
7379     }
7380   }
7381
7382   // Apply recursive replacements (BUG 0020185)
7383   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7384   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7385   {
7386     const SMDS_MeshNode* nToKeep = nnIt->second;
7387     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7388     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7389       nToKeep = nnIt_i->second;
7390     nnIt->second = nToKeep;
7391   }
7392
7393   if ( theAvoidMakingHoles )
7394   {
7395     // find elements whose topology changes
7396
7397     vector<const SMDS_MeshElement*> pbElems;
7398     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7399     for ( ; eIt != elems.end(); ++eIt )
7400     {
7401       const SMDS_MeshElement* elem = *eIt;
7402       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7403       while ( itN->more() )
7404       {
7405         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7406         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7407         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7408         {
7409           // several nodes of elem stick
7410           pbElems.push_back( elem );
7411           break;
7412         }
7413       }
7414     }
7415     // exclude from merge nodes causing spoiling element
7416     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7417     {
7418       bool nodesExcluded = false;
7419       for ( size_t i = 0; i < pbElems.size(); ++i )
7420       {
7421         size_t prevNbMergeNodes = nodeNodeMap.size();
7422         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7423              prevNbMergeNodes < nodeNodeMap.size() )
7424           nodesExcluded = true;
7425       }
7426       if ( !nodesExcluded )
7427         break;
7428     }
7429   }
7430
7431   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7432   {
7433     const SMDS_MeshNode* nToRemove = nnIt->first;
7434     const SMDS_MeshNode* nToKeep   = nnIt->second;
7435     if ( nToRemove != nToKeep )
7436     {
7437       rmNodeIds.push_back( nToRemove->GetID() );
7438       AddToSameGroups( nToKeep, nToRemove, mesh );
7439       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7440       // w/o creating node in place of merged ones.
7441       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7442       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7443         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7444           sm->SetIsAlwaysComputed( true );
7445     }
7446   }
7447
7448   // Change element nodes or remove an element
7449
7450   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7451   for ( ; eIt != elems.end(); eIt++ )
7452   {
7453     const SMDS_MeshElement* elem = *eIt;
7454     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7455
7456     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7457     if ( !keepElem )
7458       rmElemIds.push_back( elem->GetID() );
7459
7460     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7461     {
7462       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7463                                                & newElemDefs[i].myNodes[0],
7464                                                newElemDefs[i].myNodes.size() ))
7465       {
7466         if ( i == 0 )
7467         {
7468           newElemDefs[i].SetID( elem->GetID() );
7469           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7470           if ( !keepElem ) rmElemIds.pop_back();
7471         }
7472         else
7473         {
7474           newElemDefs[i].SetID( -1 );
7475         }
7476         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7477         if ( sm && newElem )
7478           sm->AddElement( newElem );
7479         if ( elem != newElem )
7480           ReplaceElemInGroups( elem, newElem, mesh );
7481       }
7482     }
7483   }
7484
7485   // Remove bad elements, then equal nodes (order important)
7486   Remove( rmElemIds, /*isNodes=*/false );
7487   Remove( rmNodeIds, /*isNodes=*/true );
7488
7489   return;
7490 }
7491
7492 //=======================================================================
7493 //function : applyMerge
7494 //purpose  : Compute new connectivity of an element after merging nodes
7495 //  \param [in] elems - the element
7496 //  \param [out] newElemDefs - definition(s) of result element(s)
7497 //  \param [inout] nodeNodeMap - nodes to merge
7498 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7499 //              after merging (but not degenerated), removes nodes causing
7500 //              the invalidity from \a nodeNodeMap.
7501 //  \return bool - true if the element should be removed
7502 //=======================================================================
7503
7504 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7505                                    vector< ElemFeatures >& newElemDefs,
7506                                    TNodeNodeMap&           nodeNodeMap,
7507                                    const bool              avoidMakingHoles )
7508 {
7509   bool toRemove = false; // to remove elem
7510   int nbResElems = 1;    // nb new elements
7511
7512   newElemDefs.resize(nbResElems);
7513   newElemDefs[0].Init( elem );
7514   newElemDefs[0].myNodes.clear();
7515
7516   set<const SMDS_MeshNode*> nodeSet;
7517   vector< const SMDS_MeshNode*>   curNodes;
7518   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7519   vector<int> iRepl;
7520
7521   const        int  nbNodes = elem->NbNodes();
7522   SMDSAbs_EntityType entity = elem->GetEntityType();
7523
7524   curNodes.resize( nbNodes );
7525   uniqueNodes.resize( nbNodes );
7526   iRepl.resize( nbNodes );
7527   int iUnique = 0, iCur = 0, nbRepl = 0;
7528
7529   // Get new seq of nodes
7530
7531   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7532   while ( itN->more() )
7533   {
7534     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7535
7536     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7537     if ( nnIt != nodeNodeMap.end() ) {
7538       n = (*nnIt).second;
7539     }
7540     curNodes[ iCur ] = n;
7541     bool isUnique = nodeSet.insert( n ).second;
7542     if ( isUnique )
7543       uniqueNodes[ iUnique++ ] = n;
7544     else
7545       iRepl[ nbRepl++ ] = iCur;
7546     iCur++;
7547   }
7548
7549   // Analyse element topology after replacement
7550
7551   int nbUniqueNodes = nodeSet.size();
7552   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7553   {
7554     toRemove = true;
7555     nbResElems = 0;
7556
7557     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7558     {
7559       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7560       int nbCorners = nbNodes / 2;
7561       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7562       {
7563         int iNext = ( iCur + 1 ) % nbCorners;
7564         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7565         {
7566           int iMedium = iCur + nbCorners;
7567           vector< const SMDS_MeshNode* >::iterator i =
7568             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7569                        uniqueNodes.end(),
7570                        curNodes[ iMedium ]);
7571           if ( i != uniqueNodes.end() )
7572           {
7573             --nbUniqueNodes;
7574             for ( ; i+1 != uniqueNodes.end(); ++i )
7575               *i = *(i+1);
7576           }
7577         }
7578       }
7579     }
7580
7581     switch ( entity )
7582     {
7583     case SMDSEntity_Polygon:
7584     case SMDSEntity_Quad_Polygon: // Polygon
7585     {
7586       ElemFeatures* elemType = & newElemDefs[0];
7587       const bool isQuad = elemType->myIsQuad;
7588       if ( isQuad )
7589         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7590           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7591
7592       // a polygon can divide into several elements
7593       vector<const SMDS_MeshNode *> polygons_nodes;
7594       vector<int> quantities;
7595       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7596       newElemDefs.resize( nbResElems );
7597       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7598       {
7599         ElemFeatures* elemType = & newElemDefs[iface];
7600         if ( iface ) elemType->Init( elem );
7601
7602         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7603         int nbNewNodes = quantities[iface];
7604         face_nodes.assign( polygons_nodes.begin() + inode,
7605                            polygons_nodes.begin() + inode + nbNewNodes );
7606         inode += nbNewNodes;
7607         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7608         {
7609           bool isValid = ( nbNewNodes % 2 == 0 );
7610           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7611             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7612           elemType->SetQuad( isValid );
7613           if ( isValid ) // put medium nodes after corners
7614             SMDS_MeshCell::applyInterlaceRev
7615               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7616                                                     nbNewNodes ), face_nodes );
7617         }
7618         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7619       }
7620       nbUniqueNodes = newElemDefs[0].myNodes.size();
7621       break;
7622     } // Polygon
7623
7624     case SMDSEntity_Polyhedra: // Polyhedral volume
7625     {
7626       if ( nbUniqueNodes >= 4 )
7627       {
7628         // each face has to be analyzed in order to check volume validity
7629         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7630         {
7631           int nbFaces = aPolyedre->NbFaces();
7632
7633           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7634           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7635           vector<const SMDS_MeshNode *>  faceNodes;
7636           poly_nodes.clear();
7637           quantities.clear();
7638
7639           for (int iface = 1; iface <= nbFaces; iface++)
7640           {
7641             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7642             faceNodes.resize( nbFaceNodes );
7643             for (int inode = 1; inode <= nbFaceNodes; inode++)
7644             {
7645               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7646               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7647               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7648                 faceNode = (*nnIt).second;
7649               faceNodes[inode - 1] = faceNode;
7650             }
7651             SimplifyFace(faceNodes, poly_nodes, quantities);
7652           }
7653
7654           if ( quantities.size() > 3 )
7655           {
7656             // TODO: remove coincident faces
7657             nbResElems = 1;
7658             nbUniqueNodes = newElemDefs[0].myNodes.size();
7659           }
7660         }
7661       }
7662     }
7663     break;
7664
7665     // Regular elements
7666     // TODO not all the possible cases are solved. Find something more generic?
7667     case SMDSEntity_Edge: //////// EDGE
7668     case SMDSEntity_Triangle: //// TRIANGLE
7669     case SMDSEntity_Quad_Triangle:
7670     case SMDSEntity_Tetra:
7671     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7672     {
7673       break;
7674     }
7675     case SMDSEntity_Quad_Edge:
7676     {
7677       break;
7678     }
7679     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7680     {
7681       if ( nbUniqueNodes < 3 )
7682         toRemove = true;
7683       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7684         toRemove = true; // opposite nodes stick
7685       else
7686         toRemove = false;
7687       break;
7688     }
7689     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7690     {
7691       //   1    5    2
7692       //    +---+---+
7693       //    |       |
7694       //   4+       +6
7695       //    |       |
7696       //    +---+---+
7697       //   0    7    3
7698       if ( nbUniqueNodes == 6 &&
7699            iRepl[0] < 4       &&
7700            ( nbRepl == 1 || iRepl[1] >= 4 ))
7701       {
7702         toRemove = false;
7703       }
7704       break;
7705     }
7706     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7707     {
7708       //   1    5    2
7709       //    +---+---+
7710       //    |       |
7711       //   4+  8+   +6
7712       //    |       |
7713       //    +---+---+
7714       //   0    7    3
7715       if ( nbUniqueNodes == 7 &&
7716            iRepl[0] < 4       &&
7717            ( nbRepl == 1 || iRepl[1] != 8 ))
7718       {
7719         toRemove = false;
7720       }
7721       break;
7722     }
7723     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7724     {
7725       if ( nbUniqueNodes == 4 ) {
7726         // ---------------------------------> tetrahedron
7727         if ( curNodes[3] == curNodes[4] &&
7728              curNodes[3] == curNodes[5] ) {
7729           // top nodes stick
7730           toRemove = false;
7731         }
7732         else if ( curNodes[0] == curNodes[1] &&
7733                   curNodes[0] == curNodes[2] ) {
7734           // bottom nodes stick: set a top before
7735           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7736           uniqueNodes[ 0 ] = curNodes [ 5 ];
7737           uniqueNodes[ 1 ] = curNodes [ 4 ];
7738           uniqueNodes[ 2 ] = curNodes [ 3 ];
7739           toRemove = false;
7740         }
7741         else if (( curNodes[0] == curNodes[3] ) +
7742                  ( curNodes[1] == curNodes[4] ) +
7743                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7744           // a lateral face turns into a line
7745           toRemove = false;
7746         }
7747       }
7748       else if ( nbUniqueNodes == 5 ) {
7749         // PENTAHEDRON --------------------> pyramid
7750         if ( curNodes[0] == curNodes[3] )
7751         {
7752           uniqueNodes[ 0 ] = curNodes[ 1 ];
7753           uniqueNodes[ 1 ] = curNodes[ 4 ];
7754           uniqueNodes[ 2 ] = curNodes[ 5 ];
7755           uniqueNodes[ 3 ] = curNodes[ 2 ];
7756           uniqueNodes[ 4 ] = curNodes[ 0 ];
7757           toRemove = false;
7758         }
7759         if ( curNodes[1] == curNodes[4] )
7760         {
7761           uniqueNodes[ 0 ] = curNodes[ 0 ];
7762           uniqueNodes[ 1 ] = curNodes[ 2 ];
7763           uniqueNodes[ 2 ] = curNodes[ 5 ];
7764           uniqueNodes[ 3 ] = curNodes[ 3 ];
7765           uniqueNodes[ 4 ] = curNodes[ 1 ];
7766           toRemove = false;
7767         }
7768         if ( curNodes[2] == curNodes[5] )
7769         {
7770           uniqueNodes[ 0 ] = curNodes[ 0 ];
7771           uniqueNodes[ 1 ] = curNodes[ 3 ];
7772           uniqueNodes[ 2 ] = curNodes[ 4 ];
7773           uniqueNodes[ 3 ] = curNodes[ 1 ];
7774           uniqueNodes[ 4 ] = curNodes[ 2 ];
7775           toRemove = false;
7776         }
7777       }
7778       break;
7779     }
7780     case SMDSEntity_Hexa:
7781     {
7782       //////////////////////////////////// HEXAHEDRON
7783       SMDS_VolumeTool hexa (elem);
7784       hexa.SetExternalNormal();
7785       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7786         //////////////////////// HEX ---> tetrahedron
7787         for ( int iFace = 0; iFace < 6; iFace++ ) {
7788           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7789           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7790               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7791               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7792             // one face turns into a point ...
7793             int  pickInd = ind[ 0 ];
7794             int iOppFace = hexa.GetOppFaceIndex( iFace );
7795             ind = hexa.GetFaceNodesIndices( iOppFace );
7796             int nbStick = 0;
7797             uniqueNodes.clear();
7798             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7799               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7800                 nbStick++;
7801               else
7802                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7803             }
7804             if ( nbStick == 1 ) {
7805               // ... and the opposite one - into a triangle.
7806               // set a top node
7807               uniqueNodes.push_back( curNodes[ pickInd ]);
7808               toRemove = false;
7809             }
7810             break;
7811           }
7812         }
7813       }
7814       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7815         //////////////////////// HEX ---> prism
7816         int nbTria = 0, iTria[3];
7817         const int *ind; // indices of face nodes
7818         // look for triangular faces
7819         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7820           ind = hexa.GetFaceNodesIndices( iFace );
7821           TIDSortedNodeSet faceNodes;
7822           for ( iCur = 0; iCur < 4; iCur++ )
7823             faceNodes.insert( curNodes[ind[iCur]] );
7824           if ( faceNodes.size() == 3 )
7825             iTria[ nbTria++ ] = iFace;
7826         }
7827         // check if triangles are opposite
7828         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7829         {
7830           // set nodes of the bottom triangle
7831           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7832           vector<int> indB;
7833           for ( iCur = 0; iCur < 4; iCur++ )
7834             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7835               indB.push_back( ind[iCur] );
7836           if ( !hexa.IsForward() )
7837             std::swap( indB[0], indB[2] );
7838           for ( iCur = 0; iCur < 3; iCur++ )
7839             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7840           // set nodes of the top triangle
7841           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7842           for ( iCur = 0; iCur < 3; ++iCur )
7843             for ( int j = 0; j < 4; ++j )
7844               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7845               {
7846                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7847                 break;
7848               }
7849           toRemove = false;
7850           break;
7851         }
7852       }
7853       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7854         //////////////////// HEXAHEDRON ---> pyramid
7855         for ( int iFace = 0; iFace < 6; iFace++ ) {
7856           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7857           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7858               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7859               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7860             // one face turns into a point ...
7861             int iOppFace = hexa.GetOppFaceIndex( iFace );
7862             ind = hexa.GetFaceNodesIndices( iOppFace );
7863             uniqueNodes.clear();
7864             for ( iCur = 0; iCur < 4; iCur++ ) {
7865               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7866                 break;
7867               else
7868                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7869             }
7870             if ( uniqueNodes.size() == 4 ) {
7871               // ... and the opposite one is a quadrangle
7872               // set a top node
7873               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7874               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7875               toRemove = false;
7876             }
7877             break;
7878           }
7879         }
7880       }
7881
7882       if ( toRemove && nbUniqueNodes > 4 ) {
7883         ////////////////// HEXAHEDRON ---> polyhedron
7884         hexa.SetExternalNormal();
7885         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7886         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7887         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7888         quantities.reserve( 6 );     quantities.clear();
7889         for ( int iFace = 0; iFace < 6; iFace++ )
7890         {
7891           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7892           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7893                curNodes[ind[1]] == curNodes[ind[3]] )
7894           {
7895             quantities.clear();
7896             break; // opposite nodes stick
7897           }
7898           nodeSet.clear();
7899           for ( iCur = 0; iCur < 4; iCur++ )
7900           {
7901             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7902               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7903           }
7904           if ( nodeSet.size() < 3 )
7905             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7906           else
7907             quantities.push_back( nodeSet.size() );
7908         }
7909         if ( quantities.size() >= 4 )
7910         {
7911           nbResElems = 1;
7912           nbUniqueNodes = poly_nodes.size();
7913           newElemDefs[0].SetPoly(true);
7914         }
7915       }
7916       break;
7917     } // case HEXAHEDRON
7918
7919     default:
7920       toRemove = true;
7921
7922     } // switch ( entity )
7923
7924     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7925     {
7926       // erase from nodeNodeMap nodes whose merge spoils elem
7927       vector< const SMDS_MeshNode* > noMergeNodes;
7928       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7929       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7930         nodeNodeMap.erase( noMergeNodes[i] );
7931     }
7932     
7933   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7934
7935   uniqueNodes.resize( nbUniqueNodes );
7936
7937   if ( !toRemove && nbResElems == 0 )
7938     nbResElems = 1;
7939
7940   newElemDefs.resize( nbResElems );
7941
7942   return !toRemove;
7943 }
7944
7945
7946 // ========================================================
7947 // class   : SortableElement
7948 // purpose : allow sorting elements basing on their nodes
7949 // ========================================================
7950 class SortableElement : public set <const SMDS_MeshElement*>
7951 {
7952 public:
7953
7954   SortableElement( const SMDS_MeshElement* theElem )
7955   {
7956     myElem = theElem;
7957     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7958     while ( nodeIt->more() )
7959       this->insert( nodeIt->next() );
7960   }
7961
7962   const SMDS_MeshElement* Get() const
7963   { return myElem; }
7964
7965 private:
7966   mutable const SMDS_MeshElement* myElem;
7967 };
7968
7969 //=======================================================================
7970 //function : FindEqualElements
7971 //purpose  : Return list of group of elements built on the same nodes.
7972 //           Search among theElements or in the whole mesh if theElements is empty
7973 //=======================================================================
7974
7975 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7976                                          TListOfListOfElementsID & theGroupsOfElementsID)
7977 {
7978   myLastCreatedElems.Clear();
7979   myLastCreatedNodes.Clear();
7980
7981   typedef map< SortableElement, int > TMapOfNodeSet;
7982   typedef list<int> TGroupOfElems;
7983
7984   if ( theElements.empty() )
7985   { // get all elements in the mesh
7986     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7987     while ( eIt->more() )
7988       theElements.insert( theElements.end(), eIt->next() );
7989   }
7990
7991   vector< TGroupOfElems > arrayOfGroups;
7992   TGroupOfElems groupOfElems;
7993   TMapOfNodeSet mapOfNodeSet;
7994
7995   TIDSortedElemSet::iterator elemIt = theElements.begin();
7996   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7997   {
7998     const SMDS_MeshElement* curElem = *elemIt;
7999     SortableElement SE(curElem);
8000     // check uniqueness
8001     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8002     if ( !pp.second ) { // one more coincident elem
8003       TMapOfNodeSet::iterator& itSE = pp.first;
8004       int ind = (*itSE).second;
8005       arrayOfGroups[ind].push_back( curElem->GetID() );
8006     }
8007     else {
8008       arrayOfGroups.push_back( groupOfElems );
8009       arrayOfGroups.back().push_back( curElem->GetID() );
8010       i++;
8011     }
8012   }
8013
8014   groupOfElems.clear();
8015   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8016   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8017   {
8018     if ( groupIt->size() > 1 ) {
8019       //groupOfElems.sort(); -- theElements is sorted already
8020       theGroupsOfElementsID.push_back( groupOfElems );
8021       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8022     }
8023   }
8024 }
8025
8026 //=======================================================================
8027 //function : MergeElements
8028 //purpose  : In each given group, substitute all elements by the first one.
8029 //=======================================================================
8030
8031 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8032 {
8033   myLastCreatedElems.Clear();
8034   myLastCreatedNodes.Clear();
8035
8036   typedef list<int> TListOfIDs;
8037   TListOfIDs rmElemIds; // IDs of elems to remove
8038
8039   SMESHDS_Mesh* aMesh = GetMeshDS();
8040
8041   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8042   while ( groupsIt != theGroupsOfElementsID.end() ) {
8043     TListOfIDs& aGroupOfElemID = *groupsIt;
8044     aGroupOfElemID.sort();
8045     int elemIDToKeep = aGroupOfElemID.front();
8046     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8047     aGroupOfElemID.pop_front();
8048     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8049     while ( idIt != aGroupOfElemID.end() ) {
8050       int elemIDToRemove = *idIt;
8051       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8052       // add the kept element in groups of removed one (PAL15188)
8053       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8054       rmElemIds.push_back( elemIDToRemove );
8055       ++idIt;
8056     }
8057     ++groupsIt;
8058   }
8059
8060   Remove( rmElemIds, false );
8061 }
8062
8063 //=======================================================================
8064 //function : MergeEqualElements
8065 //purpose  : Remove all but one of elements built on the same nodes.
8066 //=======================================================================
8067
8068 void SMESH_MeshEditor::MergeEqualElements()
8069 {
8070   TIDSortedElemSet aMeshElements; /* empty input ==
8071                                      to merge equal elements in the whole mesh */
8072   TListOfListOfElementsID aGroupsOfElementsID;
8073   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8074   MergeElements(aGroupsOfElementsID);
8075 }
8076
8077 //=======================================================================
8078 //function : findAdjacentFace
8079 //purpose  :
8080 //=======================================================================
8081
8082 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8083                                                 const SMDS_MeshNode* n2,
8084                                                 const SMDS_MeshElement* elem)
8085 {
8086   TIDSortedElemSet elemSet, avoidSet;
8087   if ( elem )
8088     avoidSet.insert ( elem );
8089   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 }
8091
8092 //=======================================================================
8093 //function : findSegment
8094 //purpose  : Return a mesh segment by two nodes one of which can be medium
8095 //=======================================================================
8096
8097 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8098                                            const SMDS_MeshNode* n2)
8099 {
8100   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8101   while ( it->more() )
8102   {
8103     const SMDS_MeshElement* seg = it->next();
8104     if ( seg->GetNodeIndex( n2 ) >= 0 )
8105       return seg;
8106   }
8107   return 0;
8108 }
8109
8110 //=======================================================================
8111 //function : FindFreeBorder
8112 //purpose  :
8113 //=======================================================================
8114
8115 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8116
8117 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8118                                        const SMDS_MeshNode*             theSecondNode,
8119                                        const SMDS_MeshNode*             theLastNode,
8120                                        list< const SMDS_MeshNode* > &   theNodes,
8121                                        list< const SMDS_MeshElement* >& theFaces)
8122 {
8123   if ( !theFirstNode || !theSecondNode )
8124     return false;
8125   // find border face between theFirstNode and theSecondNode
8126   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8127   if ( !curElem )
8128     return false;
8129
8130   theFaces.push_back( curElem );
8131   theNodes.push_back( theFirstNode );
8132   theNodes.push_back( theSecondNode );
8133
8134   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8135   TIDSortedElemSet foundElems;
8136   bool needTheLast = ( theLastNode != 0 );
8137
8138   while ( nStart != theLastNode ) {
8139     if ( nStart == theFirstNode )
8140       return !needTheLast;
8141
8142     // find all free border faces sharing form nStart
8143
8144     list< const SMDS_MeshElement* > curElemList;
8145     list< const SMDS_MeshNode* >    nStartList;
8146     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8147     while ( invElemIt->more() ) {
8148       const SMDS_MeshElement* e = invElemIt->next();
8149       if ( e == curElem || foundElems.insert( e ).second ) {
8150         // get nodes
8151         int iNode = 0, nbNodes = e->NbNodes();
8152         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8153
8154         if ( e->IsQuadratic() ) {
8155           const SMDS_VtkFace* F =
8156             dynamic_cast<const SMDS_VtkFace*>(e);
8157           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8158           // use special nodes iterator
8159           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8160           while( anIter->more() ) {
8161             nodes[ iNode++ ] = cast2Node(anIter->next());
8162           }
8163         }
8164         else {
8165           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8166           while ( nIt->more() )
8167             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8168         }
8169         nodes[ iNode ] = nodes[ 0 ];
8170         // check 2 links
8171         for ( iNode = 0; iNode < nbNodes; iNode++ )
8172           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8173                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8174               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8175           {
8176             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8177             curElemList.push_back( e );
8178           }
8179       }
8180     }
8181     // analyse the found
8182
8183     int nbNewBorders = curElemList.size();
8184     if ( nbNewBorders == 0 ) {
8185       // no free border furthermore
8186       return !needTheLast;
8187     }
8188     else if ( nbNewBorders == 1 ) {
8189       // one more element found
8190       nIgnore = nStart;
8191       nStart = nStartList.front();
8192       curElem = curElemList.front();
8193       theFaces.push_back( curElem );
8194       theNodes.push_back( nStart );
8195     }
8196     else {
8197       // several continuations found
8198       list< const SMDS_MeshElement* >::iterator curElemIt;
8199       list< const SMDS_MeshNode* >::iterator nStartIt;
8200       // check if one of them reached the last node
8201       if ( needTheLast ) {
8202         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8203              curElemIt!= curElemList.end();
8204              curElemIt++, nStartIt++ )
8205           if ( *nStartIt == theLastNode ) {
8206             theFaces.push_back( *curElemIt );
8207             theNodes.push_back( *nStartIt );
8208             return true;
8209           }
8210       }
8211       // find the best free border by the continuations
8212       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8213       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8214       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8215            curElemIt!= curElemList.end();
8216            curElemIt++, nStartIt++ )
8217       {
8218         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8219         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8220         // find one more free border
8221         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8222           cNL->clear();
8223           cFL->clear();
8224         }
8225         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8226           // choice: clear a worse one
8227           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8228           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8229           contNodes[ iWorse ].clear();
8230           contFaces[ iWorse ].clear();
8231         }
8232       }
8233       if ( contNodes[0].empty() && contNodes[1].empty() )
8234         return false;
8235
8236       // append the best free border
8237       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8238       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8239       theNodes.pop_back(); // remove nIgnore
8240       theNodes.pop_back(); // remove nStart
8241       theFaces.pop_back(); // remove curElem
8242       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8243       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8244       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8245       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8246       return true;
8247
8248     } // several continuations found
8249   } // while ( nStart != theLastNode )
8250
8251   return true;
8252 }
8253
8254 //=======================================================================
8255 //function : CheckFreeBorderNodes
8256 //purpose  : Return true if the tree nodes are on a free border
8257 //=======================================================================
8258
8259 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8260                                             const SMDS_MeshNode* theNode2,
8261                                             const SMDS_MeshNode* theNode3)
8262 {
8263   list< const SMDS_MeshNode* > nodes;
8264   list< const SMDS_MeshElement* > faces;
8265   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8266 }
8267
8268 //=======================================================================
8269 //function : SewFreeBorder
8270 //purpose  :
8271 //warning  : for border-to-side sewing theSideSecondNode is considered as
8272 //           the last side node and theSideThirdNode is not used
8273 //=======================================================================
8274
8275 SMESH_MeshEditor::Sew_Error
8276 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8277                                  const SMDS_MeshNode* theBordSecondNode,
8278                                  const SMDS_MeshNode* theBordLastNode,
8279                                  const SMDS_MeshNode* theSideFirstNode,
8280                                  const SMDS_MeshNode* theSideSecondNode,
8281                                  const SMDS_MeshNode* theSideThirdNode,
8282                                  const bool           theSideIsFreeBorder,
8283                                  const bool           toCreatePolygons,
8284                                  const bool           toCreatePolyedrs)
8285 {
8286   myLastCreatedElems.Clear();
8287   myLastCreatedNodes.Clear();
8288
8289   Sew_Error aResult = SEW_OK;
8290
8291   // ====================================
8292   //    find side nodes and elements
8293   // ====================================
8294
8295   list< const SMDS_MeshNode* >    nSide[ 2 ];
8296   list< const SMDS_MeshElement* > eSide[ 2 ];
8297   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8298   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8299
8300   // Free border 1
8301   // --------------
8302   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8303                       nSide[0], eSide[0])) {
8304     MESSAGE(" Free Border 1 not found " );
8305     aResult = SEW_BORDER1_NOT_FOUND;
8306   }
8307   if (theSideIsFreeBorder) {
8308     // Free border 2
8309     // --------------
8310     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8311                         nSide[1], eSide[1])) {
8312       MESSAGE(" Free Border 2 not found " );
8313       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8314     }
8315   }
8316   if ( aResult != SEW_OK )
8317     return aResult;
8318
8319   if (!theSideIsFreeBorder) {
8320     // Side 2
8321     // --------------
8322
8323     // -------------------------------------------------------------------------
8324     // Algo:
8325     // 1. If nodes to merge are not coincident, move nodes of the free border
8326     //    from the coord sys defined by the direction from the first to last
8327     //    nodes of the border to the correspondent sys of the side 2
8328     // 2. On the side 2, find the links most co-directed with the correspondent
8329     //    links of the free border
8330     // -------------------------------------------------------------------------
8331
8332     // 1. Since sewing may break if there are volumes to split on the side 2,
8333     //    we won't move nodes but just compute new coordinates for them
8334     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8335     TNodeXYZMap nBordXYZ;
8336     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8337     list< const SMDS_MeshNode* >::iterator nBordIt;
8338
8339     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8340     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8341     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8342     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8343     double tol2 = 1.e-8;
8344     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8345     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8346       // Need node movement.
8347
8348       // find X and Z axes to create trsf
8349       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8350       gp_Vec X = Zs ^ Zb;
8351       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8352         // Zb || Zs
8353         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8354
8355       // coord systems
8356       gp_Ax3 toBordAx( Pb1, Zb, X );
8357       gp_Ax3 fromSideAx( Ps1, Zs, X );
8358       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8359       // set trsf
8360       gp_Trsf toBordSys, fromSide2Sys;
8361       toBordSys.SetTransformation( toBordAx );
8362       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8363       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8364
8365       // move
8366       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8367         const SMDS_MeshNode* n = *nBordIt;
8368         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8369         toBordSys.Transforms( xyz );
8370         fromSide2Sys.Transforms( xyz );
8371         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8372       }
8373     }
8374     else {
8375       // just insert nodes XYZ in the nBordXYZ map
8376       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8377         const SMDS_MeshNode* n = *nBordIt;
8378         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8379       }
8380     }
8381
8382     // 2. On the side 2, find the links most co-directed with the correspondent
8383     //    links of the free border
8384
8385     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8386     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8387     sideNodes.push_back( theSideFirstNode );
8388
8389     bool hasVolumes = false;
8390     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8391     set<long> foundSideLinkIDs, checkedLinkIDs;
8392     SMDS_VolumeTool volume;
8393     //const SMDS_MeshNode* faceNodes[ 4 ];
8394
8395     const SMDS_MeshNode*    sideNode;
8396     const SMDS_MeshElement* sideElem  = 0;
8397     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8398     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8399     nBordIt = bordNodes.begin();
8400     nBordIt++;
8401     // border node position and border link direction to compare with
8402     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8403     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8404     // choose next side node by link direction or by closeness to
8405     // the current border node:
8406     bool searchByDir = ( *nBordIt != theBordLastNode );
8407     do {
8408       // find the next node on the Side 2
8409       sideNode = 0;
8410       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8411       long linkID;
8412       checkedLinkIDs.clear();
8413       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8414
8415       // loop on inverse elements of current node (prevSideNode) on the Side 2
8416       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8417       while ( invElemIt->more() )
8418       {
8419         const SMDS_MeshElement* elem = invElemIt->next();
8420         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8421         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8422         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8423         bool isVolume = volume.Set( elem );
8424         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8425         if ( isVolume ) // --volume
8426           hasVolumes = true;
8427         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8428           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8429           if(elem->IsQuadratic()) {
8430             const SMDS_VtkFace* F =
8431               dynamic_cast<const SMDS_VtkFace*>(elem);
8432             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8433             // use special nodes iterator
8434             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8435             while( anIter->more() ) {
8436               nodes[ iNode ] = cast2Node(anIter->next());
8437               if ( nodes[ iNode++ ] == prevSideNode )
8438                 iPrevNode = iNode - 1;
8439             }
8440           }
8441           else {
8442             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8443             while ( nIt->more() ) {
8444               nodes[ iNode ] = cast2Node( nIt->next() );
8445               if ( nodes[ iNode++ ] == prevSideNode )
8446                 iPrevNode = iNode - 1;
8447             }
8448           }
8449           // there are 2 links to check
8450           nbNodes = 2;
8451         }
8452         else // --edge
8453           continue;
8454         // loop on links, to be precise, on the second node of links
8455         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8456           const SMDS_MeshNode* n = nodes[ iNode ];
8457           if ( isVolume ) {
8458             if ( !volume.IsLinked( n, prevSideNode ))
8459               continue;
8460           }
8461           else {
8462             if ( iNode ) // a node before prevSideNode
8463               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8464             else         // a node after prevSideNode
8465               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8466           }
8467           // check if this link was already used
8468           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8469           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8470           if (!isJustChecked &&
8471               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8472           {
8473             // test a link geometrically
8474             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8475             bool linkIsBetter = false;
8476             double dot = 0.0, dist = 0.0;
8477             if ( searchByDir ) { // choose most co-directed link
8478               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8479               linkIsBetter = ( dot > maxDot );
8480             }
8481             else { // choose link with the node closest to bordPos
8482               dist = ( nextXYZ - bordPos ).SquareModulus();
8483               linkIsBetter = ( dist < minDist );
8484             }
8485             if ( linkIsBetter ) {
8486               maxDot = dot;
8487               minDist = dist;
8488               linkID = iLink;
8489               sideNode = n;
8490               sideElem = elem;
8491             }
8492           }
8493         }
8494       } // loop on inverse elements of prevSideNode
8495
8496       if ( !sideNode ) {
8497         MESSAGE(" Can't find path by links of the Side 2 ");
8498         return SEW_BAD_SIDE_NODES;
8499       }
8500       sideNodes.push_back( sideNode );
8501       sideElems.push_back( sideElem );
8502       foundSideLinkIDs.insert ( linkID );
8503       prevSideNode = sideNode;
8504
8505       if ( *nBordIt == theBordLastNode )
8506         searchByDir = false;
8507       else {
8508         // find the next border link to compare with
8509         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8510         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8511         // move to next border node if sideNode is before forward border node (bordPos)
8512         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8513           prevBordNode = *nBordIt;
8514           nBordIt++;
8515           bordPos = nBordXYZ[ *nBordIt ];
8516           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8517           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8518         }
8519       }
8520     }
8521     while ( sideNode != theSideSecondNode );
8522
8523     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8524       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8525       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8526     }
8527   } // end nodes search on the side 2
8528
8529   // ============================
8530   // sew the border to the side 2
8531   // ============================
8532
8533   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8534   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8535
8536   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8537   if ( toMergeConformal && toCreatePolygons )
8538   {
8539     // do not merge quadrangles if polygons are OK (IPAL0052824)
8540     eIt[0] = eSide[0].begin();
8541     eIt[1] = eSide[1].begin();
8542     bool allQuads[2] = { true, true };
8543     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8544       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8545         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8546     }
8547     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8548   }
8549
8550   TListOfListOfNodes nodeGroupsToMerge;
8551   if (( toMergeConformal ) ||
8552       ( theSideIsFreeBorder && !theSideThirdNode )) {
8553
8554     // all nodes are to be merged
8555
8556     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8557          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8558          nIt[0]++, nIt[1]++ )
8559     {
8560       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8561       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8562       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8563     }
8564   }
8565   else {
8566
8567     // insert new nodes into the border and the side to get equal nb of segments
8568
8569     // get normalized parameters of nodes on the borders
8570     vector< double > param[ 2 ];
8571     param[0].resize( maxNbNodes );
8572     param[1].resize( maxNbNodes );
8573     int iNode, iBord;
8574     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8575       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8576       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8577       const SMDS_MeshNode* nPrev = *nIt;
8578       double bordLength = 0;
8579       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8580         const SMDS_MeshNode* nCur = *nIt;
8581         gp_XYZ segment (nCur->X() - nPrev->X(),
8582                         nCur->Y() - nPrev->Y(),
8583                         nCur->Z() - nPrev->Z());
8584         double segmentLen = segment.Modulus();
8585         bordLength += segmentLen;
8586         param[ iBord ][ iNode ] = bordLength;
8587         nPrev = nCur;
8588       }
8589       // normalize within [0,1]
8590       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8591         param[ iBord ][ iNode ] /= bordLength;
8592       }
8593     }
8594
8595     // loop on border segments
8596     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8597     int i[ 2 ] = { 0, 0 };
8598     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8599     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8600
8601     TElemOfNodeListMap insertMap;
8602     TElemOfNodeListMap::iterator insertMapIt;
8603     // insertMap is
8604     // key:   elem to insert nodes into
8605     // value: 2 nodes to insert between + nodes to be inserted
8606     do {
8607       bool next[ 2 ] = { false, false };
8608
8609       // find min adjacent segment length after sewing
8610       double nextParam = 10., prevParam = 0;
8611       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8612         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8613           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8614         if ( i[ iBord ] > 0 )
8615           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8616       }
8617       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8618       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8620
8621       // choose to insert or to merge nodes
8622       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8623       if ( Abs( du ) <= minSegLen * 0.2 ) {
8624         // merge
8625         // ------
8626         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8627         const SMDS_MeshNode* n0 = *nIt[0];
8628         const SMDS_MeshNode* n1 = *nIt[1];
8629         nodeGroupsToMerge.back().push_back( n1 );
8630         nodeGroupsToMerge.back().push_back( n0 );
8631         // position of node of the border changes due to merge
8632         param[ 0 ][ i[0] ] += du;
8633         // move n1 for the sake of elem shape evaluation during insertion.
8634         // n1 will be removed by MergeNodes() anyway
8635         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8636         next[0] = next[1] = true;
8637       }
8638       else {
8639         // insert
8640         // ------
8641         int intoBord = ( du < 0 ) ? 0 : 1;
8642         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8643         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8644         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8645         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8646         if ( intoBord == 1 ) {
8647           // move node of the border to be on a link of elem of the side
8648           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8649           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8650           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8651           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8652           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8653         }
8654         insertMapIt = insertMap.find( elem );
8655         bool  notFound = ( insertMapIt == insertMap.end() );
8656         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8657         if ( otherLink ) {
8658           // insert into another link of the same element:
8659           // 1. perform insertion into the other link of the elem
8660           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8661           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8662           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8663           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8664           // 2. perform insertion into the link of adjacent faces
8665           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8666             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8667           }
8668           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8669             InsertNodesIntoLink( seg, n12, n22, nodeList );
8670           }
8671           if (toCreatePolyedrs) {
8672             // perform insertion into the links of adjacent volumes
8673             UpdateVolumes(n12, n22, nodeList);
8674           }
8675           // 3. find an element appeared on n1 and n2 after the insertion
8676           insertMap.erase( elem );
8677           elem = findAdjacentFace( n1, n2, 0 );
8678         }
8679         if ( notFound || otherLink ) {
8680           // add element and nodes of the side into the insertMap
8681           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8682           (*insertMapIt).second.push_back( n1 );
8683           (*insertMapIt).second.push_back( n2 );
8684         }
8685         // add node to be inserted into elem
8686         (*insertMapIt).second.push_back( nIns );
8687         next[ 1 - intoBord ] = true;
8688       }
8689
8690       // go to the next segment
8691       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8692         if ( next[ iBord ] ) {
8693           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8694             eIt[ iBord ]++;
8695           nPrev[ iBord ] = *nIt[ iBord ];
8696           nIt[ iBord ]++; i[ iBord ]++;
8697         }
8698       }
8699     }
8700     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8701
8702     // perform insertion of nodes into elements
8703
8704     for (insertMapIt = insertMap.begin();
8705          insertMapIt != insertMap.end();
8706          insertMapIt++ )
8707     {
8708       const SMDS_MeshElement* elem = (*insertMapIt).first;
8709       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8710       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8711       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8712
8713       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8714
8715       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8716         InsertNodesIntoLink( seg, n1, n2, nodeList );
8717       }
8718
8719       if ( !theSideIsFreeBorder ) {
8720         // look for and insert nodes into the faces adjacent to elem
8721         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8722           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8723         }
8724       }
8725       if (toCreatePolyedrs) {
8726         // perform insertion into the links of adjacent volumes
8727         UpdateVolumes(n1, n2, nodeList);
8728       }
8729     }
8730   } // end: insert new nodes
8731
8732   MergeNodes ( nodeGroupsToMerge );
8733
8734
8735   // Remove coincident segments
8736
8737   // get new segments
8738   TIDSortedElemSet segments;
8739   SMESH_SequenceOfElemPtr newFaces;
8740   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8741   {
8742     if ( !myLastCreatedElems(i) ) continue;
8743     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8744       segments.insert( segments.end(), myLastCreatedElems(i) );
8745     else
8746       newFaces.Append( myLastCreatedElems(i) );
8747   }
8748   // get segments adjacent to merged nodes
8749   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8750   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8751   {
8752     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8753     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8754     while ( segIt->more() )
8755       segments.insert( segIt->next() );
8756   }
8757
8758   // find coincident
8759   TListOfListOfElementsID equalGroups;
8760   if ( !segments.empty() )
8761     FindEqualElements( segments, equalGroups );
8762   if ( !equalGroups.empty() )
8763   {
8764     // remove from segments those that will be removed
8765     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8766     for ( ; itGroups != equalGroups.end(); ++itGroups )
8767     {
8768       list< int >& group = *itGroups;
8769       list< int >::iterator id = group.begin();
8770       for ( ++id; id != group.end(); ++id )
8771         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8772           segments.erase( seg );
8773     }
8774     // remove equal segments
8775     MergeElements( equalGroups );
8776
8777     // restore myLastCreatedElems
8778     myLastCreatedElems = newFaces;
8779     TIDSortedElemSet::iterator seg = segments.begin();
8780     for ( ; seg != segments.end(); ++seg )
8781       myLastCreatedElems.Append( *seg );
8782   }
8783
8784   return aResult;
8785 }
8786
8787 //=======================================================================
8788 //function : InsertNodesIntoLink
8789 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8790 //           and theBetweenNode2 and split theElement
8791 //=======================================================================
8792
8793 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8794                                            const SMDS_MeshNode*        theBetweenNode1,
8795                                            const SMDS_MeshNode*        theBetweenNode2,
8796                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8797                                            const bool                  toCreatePoly)
8798 {
8799   if ( !theElement ) return;
8800
8801   SMESHDS_Mesh *aMesh = GetMeshDS();
8802   vector<const SMDS_MeshElement*> newElems;
8803
8804   if ( theElement->GetType() == SMDSAbs_Edge )
8805   {
8806     theNodesToInsert.push_front( theBetweenNode1 );
8807     theNodesToInsert.push_back ( theBetweenNode2 );
8808     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8809     const SMDS_MeshNode* n1 = *n;
8810     for ( ++n; n != theNodesToInsert.end(); ++n )
8811     {
8812       const SMDS_MeshNode* n2 = *n;
8813       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8814         AddToSameGroups( seg, theElement, aMesh );
8815       else
8816         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8817       n1 = n2;
8818     }
8819     theNodesToInsert.pop_front();
8820     theNodesToInsert.pop_back();
8821
8822     if ( theElement->IsQuadratic() ) // add a not split part
8823     {
8824       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8825                                           theElement->end_nodes() );
8826       int iOther = 0, nbN = nodes.size();
8827       for ( ; iOther < nbN; ++iOther )
8828         if ( nodes[iOther] != theBetweenNode1 &&
8829              nodes[iOther] != theBetweenNode2 )
8830           break;
8831       if      ( iOther == 0 )
8832       {
8833         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8834           AddToSameGroups( seg, theElement, aMesh );
8835         else
8836           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8837       }
8838       else if ( iOther == 2 )
8839       {
8840         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8841           AddToSameGroups( seg, theElement, aMesh );
8842         else
8843           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8844       }
8845     }
8846     // treat new elements
8847     for ( size_t i = 0; i < newElems.size(); ++i )
8848       if ( newElems[i] )
8849       {
8850         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8851         myLastCreatedElems.Append( newElems[i] );
8852       }
8853     ReplaceElemInGroups( theElement, newElems, aMesh );
8854     aMesh->RemoveElement( theElement );
8855     return;
8856
8857   } // if ( theElement->GetType() == SMDSAbs_Edge )
8858
8859   const SMDS_MeshElement* theFace = theElement;
8860   if ( theFace->GetType() != SMDSAbs_Face ) return;
8861
8862   // find indices of 2 link nodes and of the rest nodes
8863   int iNode = 0, il1, il2, i3, i4;
8864   il1 = il2 = i3 = i4 = -1;
8865   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8866
8867   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8868   while ( nodeIt->more() ) {
8869     const SMDS_MeshNode* n = nodeIt->next();
8870     if ( n == theBetweenNode1 )
8871       il1 = iNode;
8872     else if ( n == theBetweenNode2 )
8873       il2 = iNode;
8874     else if ( i3 < 0 )
8875       i3 = iNode;
8876     else
8877       i4 = iNode;
8878     nodes[ iNode++ ] = n;
8879   }
8880   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8881     return ;
8882
8883   // arrange link nodes to go one after another regarding the face orientation
8884   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8885   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8886   if ( reverse ) {
8887     iNode = il1;
8888     il1 = il2;
8889     il2 = iNode;
8890     aNodesToInsert.reverse();
8891   }
8892   // check that not link nodes of a quadrangles are in good order
8893   int nbFaceNodes = theFace->NbNodes();
8894   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8895     iNode = i3;
8896     i3 = i4;
8897     i4 = iNode;
8898   }
8899
8900   if (toCreatePoly || theFace->IsPoly()) {
8901
8902     iNode = 0;
8903     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8904
8905     // add nodes of face up to first node of link
8906     bool isFLN = false;
8907
8908     if ( theFace->IsQuadratic() ) {
8909       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8910       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8911       // use special nodes iterator
8912       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8913       while( anIter->more()  && !isFLN ) {
8914         const SMDS_MeshNode* n = cast2Node(anIter->next());
8915         poly_nodes[iNode++] = n;
8916         if (n == nodes[il1]) {
8917           isFLN = true;
8918         }
8919       }
8920       // add nodes to insert
8921       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922       for (; nIt != aNodesToInsert.end(); nIt++) {
8923         poly_nodes[iNode++] = *nIt;
8924       }
8925       // add nodes of face starting from last node of link
8926       while ( anIter->more() ) {
8927         poly_nodes[iNode++] = cast2Node(anIter->next());
8928       }
8929     }
8930     else {
8931       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8932       while ( nodeIt->more() && !isFLN ) {
8933         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8934         poly_nodes[iNode++] = n;
8935         if (n == nodes[il1]) {
8936           isFLN = true;
8937         }
8938       }
8939       // add nodes to insert
8940       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8941       for (; nIt != aNodesToInsert.end(); nIt++) {
8942         poly_nodes[iNode++] = *nIt;
8943       }
8944       // add nodes of face starting from last node of link
8945       while ( nodeIt->more() ) {
8946         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8947         poly_nodes[iNode++] = n;
8948       }
8949     }
8950
8951     // make a new face
8952     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8953   }
8954
8955   else if ( !theFace->IsQuadratic() )
8956   {
8957     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8958     int nbLinkNodes = 2 + aNodesToInsert.size();
8959     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8960     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8961     linkNodes[ 0 ] = nodes[ il1 ];
8962     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8963     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8964     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8965       linkNodes[ iNode++ ] = *nIt;
8966     }
8967     // decide how to split a quadrangle: compare possible variants
8968     // and choose which of splits to be a quadrangle
8969     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8970     if ( nbFaceNodes == 3 ) {
8971       iBestQuad = nbSplits;
8972       i4 = i3;
8973     }
8974     else if ( nbFaceNodes == 4 ) {
8975       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8976       double aBestRate = DBL_MAX;
8977       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8978         i1 = 0; i2 = 1;
8979         double aBadRate = 0;
8980         // evaluate elements quality
8981         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8982           if ( iSplit == iQuad ) {
8983             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8984                                    linkNodes[ i2++ ],
8985                                    nodes[ i3 ],
8986                                    nodes[ i4 ]);
8987             aBadRate += getBadRate( &quad, aCrit );
8988           }
8989           else {
8990             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8991                                    linkNodes[ i2++ ],
8992                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8993             aBadRate += getBadRate( &tria, aCrit );
8994           }
8995         }
8996         // choice
8997         if ( aBadRate < aBestRate ) {
8998           iBestQuad = iQuad;
8999           aBestRate = aBadRate;
9000         }
9001       }
9002     }
9003
9004     // create new elements
9005     i1 = 0; i2 = 1;
9006     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9007     {
9008       if ( iSplit == iBestQuad )
9009         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010                                             linkNodes[ i2++ ],
9011                                             nodes[ i3 ],
9012                                             nodes[ i4 ]));
9013       else
9014         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9015                                             linkNodes[ i2++ ],
9016                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9017     }
9018
9019     const SMDS_MeshNode* newNodes[ 4 ];
9020     newNodes[ 0 ] = linkNodes[ i1 ];
9021     newNodes[ 1 ] = linkNodes[ i2 ];
9022     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9023     newNodes[ 3 ] = nodes[ i4 ];
9024     if (iSplit == iBestQuad)
9025       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9026     else
9027       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9028
9029   } // end if(!theFace->IsQuadratic())
9030
9031   else { // theFace is quadratic
9032     // we have to split theFace on simple triangles and one simple quadrangle
9033     int tmp = il1/2;
9034     int nbshift = tmp*2;
9035     // shift nodes in nodes[] by nbshift
9036     int i,j;
9037     for(i=0; i<nbshift; i++) {
9038       const SMDS_MeshNode* n = nodes[0];
9039       for(j=0; j<nbFaceNodes-1; j++) {
9040         nodes[j] = nodes[j+1];
9041       }
9042       nodes[nbFaceNodes-1] = n;
9043     }
9044     il1 = il1 - nbshift;
9045     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9046     //   n0      n1     n2    n0      n1     n2
9047     //     +-----+-----+        +-----+-----+
9048     //      \         /         |           |
9049     //       \       /          |           |
9050     //      n5+     +n3       n7+           +n3
9051     //         \   /            |           |
9052     //          \ /             |           |
9053     //           +              +-----+-----+
9054     //           n4           n6      n5     n4
9055
9056     // create new elements
9057     int n1,n2,n3;
9058     if ( nbFaceNodes == 6 ) { // quadratic triangle
9059       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9060       if ( theFace->IsMediumNode(nodes[il1]) ) {
9061         // create quadrangle
9062         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9063         n1 = 1;
9064         n2 = 2;
9065         n3 = 3;
9066       }
9067       else {
9068         // create quadrangle
9069         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9070         n1 = 0;
9071         n2 = 1;
9072         n3 = 5;
9073       }
9074     }
9075     else { // nbFaceNodes==8 - quadratic quadrangle
9076       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9077       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9078       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9079       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9080         // create quadrangle
9081         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9082         n1 = 1;
9083         n2 = 2;
9084         n3 = 3;
9085       }
9086       else {
9087         // create quadrangle
9088         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9089         n1 = 0;
9090         n2 = 1;
9091         n3 = 7;
9092       }
9093     }
9094     // create needed triangles using n1,n2,n3 and inserted nodes
9095     int nbn = 2 + aNodesToInsert.size();
9096     vector<const SMDS_MeshNode*> aNodes(nbn);
9097     aNodes[0    ] = nodes[n1];
9098     aNodes[nbn-1] = nodes[n2];
9099     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9100     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9101       aNodes[iNode++] = *nIt;
9102     }
9103     for ( i = 1; i < nbn; i++ )
9104       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9105   }
9106
9107   // remove the old face
9108   for ( size_t i = 0; i < newElems.size(); ++i )
9109     if ( newElems[i] )
9110     {
9111       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9112       myLastCreatedElems.Append( newElems[i] );
9113     }
9114   ReplaceElemInGroups( theFace, newElems, aMesh );
9115   aMesh->RemoveElement(theFace);
9116
9117 } // InsertNodesIntoLink()
9118
9119 //=======================================================================
9120 //function : UpdateVolumes
9121 //purpose  :
9122 //=======================================================================
9123
9124 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9125                                       const SMDS_MeshNode*        theBetweenNode2,
9126                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9127 {
9128   myLastCreatedElems.Clear();
9129   myLastCreatedNodes.Clear();
9130
9131   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9132   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9133     const SMDS_MeshElement* elem = invElemIt->next();
9134
9135     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9136     SMDS_VolumeTool aVolume (elem);
9137     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9138       continue;
9139
9140     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9141     int iface, nbFaces = aVolume.NbFaces();
9142     vector<const SMDS_MeshNode *> poly_nodes;
9143     vector<int> quantities (nbFaces);
9144
9145     for (iface = 0; iface < nbFaces; iface++) {
9146       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9147       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9148       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9149
9150       for (int inode = 0; inode < nbFaceNodes; inode++) {
9151         poly_nodes.push_back(faceNodes[inode]);
9152
9153         if (nbInserted == 0) {
9154           if (faceNodes[inode] == theBetweenNode1) {
9155             if (faceNodes[inode + 1] == theBetweenNode2) {
9156               nbInserted = theNodesToInsert.size();
9157
9158               // add nodes to insert
9159               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9160               for (; nIt != theNodesToInsert.end(); nIt++) {
9161                 poly_nodes.push_back(*nIt);
9162               }
9163             }
9164           }
9165           else if (faceNodes[inode] == theBetweenNode2) {
9166             if (faceNodes[inode + 1] == theBetweenNode1) {
9167               nbInserted = theNodesToInsert.size();
9168
9169               // add nodes to insert in reversed order
9170               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9171               nIt--;
9172               for (; nIt != theNodesToInsert.begin(); nIt--) {
9173                 poly_nodes.push_back(*nIt);
9174               }
9175               poly_nodes.push_back(*nIt);
9176             }
9177           }
9178           else {
9179           }
9180         }
9181       }
9182       quantities[iface] = nbFaceNodes + nbInserted;
9183     }
9184
9185     // Replace the volume
9186     SMESHDS_Mesh *aMesh = GetMeshDS();
9187
9188     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9189     {
9190       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9191       myLastCreatedElems.Append( newElem );
9192       ReplaceElemInGroups( elem, newElem, aMesh );
9193     }
9194     aMesh->RemoveElement( elem );
9195   }
9196 }
9197
9198 namespace
9199 {
9200   //================================================================================
9201   /*!
9202    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9203    */
9204   //================================================================================
9205
9206   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9207                            vector<const SMDS_MeshNode *> & nodes,
9208                            vector<int> &                   nbNodeInFaces )
9209   {
9210     nodes.clear();
9211     nbNodeInFaces.clear();
9212     SMDS_VolumeTool vTool ( elem );
9213     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9214     {
9215       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9216       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9217       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9218     }
9219   }
9220 }
9221
9222 //=======================================================================
9223 /*!
9224  * \brief Convert elements contained in a sub-mesh to quadratic
9225  * \return int - nb of checked elements
9226  */
9227 //=======================================================================
9228
9229 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9230                                              SMESH_MesherHelper& theHelper,
9231                                              const bool          theForce3d)
9232 {
9233   MESSAGE("convertElemToQuadratic");
9234   int nbElem = 0;
9235   if( !theSm ) return nbElem;
9236
9237   vector<int> nbNodeInFaces;
9238   vector<const SMDS_MeshNode *> nodes;
9239   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9240   while(ElemItr->more())
9241   {
9242     nbElem++;
9243     const SMDS_MeshElement* elem = ElemItr->next();
9244     if( !elem ) continue;
9245
9246     // analyse a necessity of conversion
9247     const SMDSAbs_ElementType aType = elem->GetType();
9248     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9249       continue;
9250     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9251     bool hasCentralNodes = false;
9252     if ( elem->IsQuadratic() )
9253     {
9254       bool alreadyOK;
9255       switch ( aGeomType ) {
9256       case SMDSEntity_Quad_Triangle:
9257       case SMDSEntity_Quad_Quadrangle:
9258       case SMDSEntity_Quad_Hexa:
9259       case SMDSEntity_Quad_Penta:
9260         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9261
9262       case SMDSEntity_BiQuad_Triangle:
9263       case SMDSEntity_BiQuad_Quadrangle:
9264       case SMDSEntity_TriQuad_Hexa:
9265       case SMDSEntity_BiQuad_Penta:
9266         alreadyOK = theHelper.GetIsBiQuadratic();
9267         hasCentralNodes = true;
9268         break;
9269       default:
9270         alreadyOK = true;
9271       }
9272       // take into account already present medium nodes
9273       switch ( aType ) {
9274       case SMDSAbs_Volume:
9275         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9276       case SMDSAbs_Face:
9277         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9278       case SMDSAbs_Edge:
9279         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9280       default:;
9281       }
9282       if ( alreadyOK )
9283         continue;
9284     }
9285     // get elem data needed to re-create it
9286     //
9287     const int id      = elem->GetID();
9288     const int nbNodes = elem->NbCornerNodes();
9289     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9290     if ( aGeomType == SMDSEntity_Polyhedra )
9291       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9292     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9293       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9294
9295     // remove a linear element
9296     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9297
9298     // remove central nodes of biquadratic elements (biquad->quad conversion)
9299     if ( hasCentralNodes )
9300       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9301         if ( nodes[i]->NbInverseElements() == 0 )
9302           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9303
9304     const SMDS_MeshElement* NewElem = 0;
9305
9306     switch( aType )
9307     {
9308     case SMDSAbs_Edge :
9309       {
9310         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9311         break;
9312       }
9313     case SMDSAbs_Face :
9314       {
9315         switch(nbNodes)
9316         {
9317         case 3:
9318           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9319           break;
9320         case 4:
9321           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9322           break;
9323         default:
9324           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9325         }
9326         break;
9327       }
9328     case SMDSAbs_Volume :
9329       {
9330         switch( aGeomType )
9331         {
9332         case SMDSEntity_Tetra:
9333           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9334           break;
9335         case SMDSEntity_Pyramid:
9336           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9337           break;
9338         case SMDSEntity_Penta:
9339         case SMDSEntity_Quad_Penta:
9340         case SMDSEntity_BiQuad_Penta:
9341           MESSAGE("--- " << aGeomType);
9342           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9343           break;
9344         case SMDSEntity_Hexa:
9345         case SMDSEntity_Quad_Hexa:
9346         case SMDSEntity_TriQuad_Hexa:
9347           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9349           break;
9350         case SMDSEntity_Hexagonal_Prism:
9351         default:
9352           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9353         }
9354         break;
9355       }
9356     default :
9357       continue;
9358     }
9359     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9360     if( NewElem && NewElem->getshapeId() < 1 )
9361       theSm->AddElement( NewElem );
9362   }
9363   return nbElem;
9364 }
9365 //=======================================================================
9366 //function : ConvertToQuadratic
9367 //purpose  :
9368 //=======================================================================
9369
9370 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9371 {
9372   MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9373   SMESHDS_Mesh* meshDS = GetMeshDS();
9374
9375   SMESH_MesherHelper aHelper(*myMesh);
9376
9377   aHelper.SetIsQuadratic( true );
9378   aHelper.SetIsBiQuadratic( theToBiQuad );
9379   aHelper.SetElementsOnShape(true);
9380   aHelper.ToFixNodeParameters( true );
9381
9382   // convert elements assigned to sub-meshes
9383   int nbCheckedElems = 0;
9384   if ( myMesh->HasShapeToMesh() )
9385   {
9386     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9387     {
9388       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9389       while ( smIt->more() ) {
9390         SMESH_subMesh* sm = smIt->next();
9391         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9392           aHelper.SetSubShape( sm->GetSubShape() );
9393           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9394         }
9395       }
9396     }
9397   }
9398
9399   // convert elements NOT assigned to sub-meshes
9400   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9401   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9402   {
9403     aHelper.SetElementsOnShape(false);
9404     SMESHDS_SubMesh *smDS = 0;
9405
9406     // convert edges
9407     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9408     while( aEdgeItr->more() )
9409     {
9410       const SMDS_MeshEdge* edge = aEdgeItr->next();
9411       if ( !edge->IsQuadratic() )
9412       {
9413         int                  id = edge->GetID();
9414         const SMDS_MeshNode* n1 = edge->GetNode(0);
9415         const SMDS_MeshNode* n2 = edge->GetNode(1);
9416
9417         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9418
9419         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9420         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9421       }
9422       else
9423       {
9424         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9425       }
9426     }
9427
9428     // convert faces
9429     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9430     while( aFaceItr->more() )
9431     {
9432       const SMDS_MeshFace* face = aFaceItr->next();
9433       if ( !face ) continue;
9434       
9435       const SMDSAbs_EntityType type = face->GetEntityType();
9436       bool alreadyOK;
9437       switch( type )
9438       {
9439       case SMDSEntity_Quad_Triangle:
9440       case SMDSEntity_Quad_Quadrangle:
9441         alreadyOK = !theToBiQuad;
9442         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443         break;
9444       case SMDSEntity_BiQuad_Triangle:
9445       case SMDSEntity_BiQuad_Quadrangle:
9446         alreadyOK = theToBiQuad;
9447         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9448         break;
9449       default: alreadyOK = false;
9450       }
9451       if ( alreadyOK )
9452         continue;
9453
9454       const int id = face->GetID();
9455       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9456
9457       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9458
9459       SMDS_MeshFace * NewFace = 0;
9460       switch( type )
9461       {
9462       case SMDSEntity_Triangle:
9463       case SMDSEntity_Quad_Triangle:
9464       case SMDSEntity_BiQuad_Triangle:
9465         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9466         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9467           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9468         break;
9469
9470       case SMDSEntity_Quadrangle:
9471       case SMDSEntity_Quad_Quadrangle:
9472       case SMDSEntity_BiQuad_Quadrangle:
9473         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9475           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9476         break;
9477
9478       default:;
9479         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9480       }
9481       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9482     }
9483
9484     // convert volumes
9485     vector<int> nbNodeInFaces;
9486     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9487     while(aVolumeItr->more())
9488     {
9489       const SMDS_MeshVolume* volume = aVolumeItr->next();
9490       if ( !volume ) continue;
9491
9492       const SMDSAbs_EntityType type = volume->GetEntityType();
9493       if ( volume->IsQuadratic() )
9494       {
9495         bool alreadyOK;
9496         switch ( type )
9497         {
9498         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9499         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9500         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9501         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9502         default:                      alreadyOK = true;
9503         }
9504         if ( alreadyOK )
9505         {
9506           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9507           continue;
9508         }
9509       }
9510       const int id = volume->GetID();
9511       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9512       if ( type == SMDSEntity_Polyhedra )
9513         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9514       else if ( type == SMDSEntity_Hexagonal_Prism )
9515         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9516
9517       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9518
9519       SMDS_MeshVolume * NewVolume = 0;
9520       switch ( type )
9521       {
9522       case SMDSEntity_Tetra:
9523         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9524         break;
9525       case SMDSEntity_Hexa:
9526       case SMDSEntity_Quad_Hexa:
9527       case SMDSEntity_TriQuad_Hexa:
9528         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9530         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9531           if ( nodes[i]->NbInverseElements() == 0 )
9532             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9533         break;
9534       case SMDSEntity_Pyramid:
9535         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9536                                       nodes[3], nodes[4], id, theForce3d);
9537         break;
9538       case SMDSEntity_Penta:
9539       case SMDSEntity_Quad_Penta:
9540       case SMDSEntity_BiQuad_Penta:
9541         MESSAGE("--- " << type);
9542         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9543                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9544         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9545           if ( nodes[i]->NbInverseElements() == 0 )
9546             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9547         break;
9548       case SMDSEntity_Hexagonal_Prism:
9549       default:
9550         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9551       }
9552       ReplaceElemInGroups(volume, NewVolume, meshDS);
9553     }
9554   }
9555
9556   if ( !theForce3d )
9557   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9558     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9559     // aHelper.FixQuadraticElements(myError);
9560     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9561   }
9562 }
9563
9564 //================================================================================
9565 /*!
9566  * \brief Makes given elements quadratic
9567  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9568  *  \param theElements - elements to make quadratic
9569  */
9570 //================================================================================
9571
9572 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9573                                           TIDSortedElemSet& theElements,
9574                                           const bool        theToBiQuad)
9575 {
9576   if ( theElements.empty() ) return;
9577
9578   // we believe that all theElements are of the same type
9579   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9580
9581   // get all nodes shared by theElements
9582   TIDSortedNodeSet allNodes;
9583   TIDSortedElemSet::iterator eIt = theElements.begin();
9584   for ( ; eIt != theElements.end(); ++eIt )
9585     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9586
9587   // complete theElements with elements of lower dim whose all nodes are in allNodes
9588
9589   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9590   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9591   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9592   for ( ; nIt != allNodes.end(); ++nIt )
9593   {
9594     const SMDS_MeshNode* n = *nIt;
9595     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9596     while ( invIt->more() )
9597     {
9598       const SMDS_MeshElement*      e = invIt->next();
9599       const SMDSAbs_ElementType type = e->GetType();
9600       if ( e->IsQuadratic() )
9601       {
9602         quadAdjacentElems[ type ].insert( e );
9603
9604         bool alreadyOK;
9605         switch ( e->GetEntityType() ) {
9606         case SMDSEntity_Quad_Triangle:
9607         case SMDSEntity_Quad_Quadrangle:
9608         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9609         case SMDSEntity_BiQuad_Triangle:
9610         case SMDSEntity_BiQuad_Quadrangle:
9611         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9612         default:                           alreadyOK = true;
9613         }
9614         if ( alreadyOK )
9615           continue;
9616       }
9617       if ( type >= elemType )
9618         continue; // same type or more complex linear element
9619
9620       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9621         continue; // e is already checked
9622
9623       // check nodes
9624       bool allIn = true;
9625       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9626       while ( nodeIt->more() && allIn )
9627         allIn = allNodes.count( nodeIt->next() );
9628       if ( allIn )
9629         theElements.insert(e );
9630     }
9631   }
9632
9633   SMESH_MesherHelper helper(*myMesh);
9634   helper.SetIsQuadratic( true );
9635   helper.SetIsBiQuadratic( theToBiQuad );
9636
9637   // add links of quadratic adjacent elements to the helper
9638
9639   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9640     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9641           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9642     {
9643       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9644     }
9645   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9646     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9647           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9648     {
9649       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9650     }
9651   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9652     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9653           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9654     {
9655       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9656     }
9657
9658   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9659
9660   SMESHDS_Mesh*  meshDS = GetMeshDS();
9661   SMESHDS_SubMesh* smDS = 0;
9662   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9663   {
9664     const SMDS_MeshElement* elem = *eIt;
9665
9666     bool alreadyOK;
9667     int nbCentralNodes = 0;
9668     switch ( elem->GetEntityType() ) {
9669       // linear convertible
9670     case SMDSEntity_Edge:
9671     case SMDSEntity_Triangle:
9672     case SMDSEntity_Quadrangle:
9673     case SMDSEntity_Tetra:
9674     case SMDSEntity_Pyramid:
9675     case SMDSEntity_Hexa:
9676     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9677       // quadratic that can become bi-quadratic
9678     case SMDSEntity_Quad_Triangle:
9679     case SMDSEntity_Quad_Quadrangle:
9680     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9681       // bi-quadratic
9682     case SMDSEntity_BiQuad_Triangle:
9683     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9684     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9685       // the rest
9686     default:                           alreadyOK = true;
9687     }
9688     if ( alreadyOK ) continue;
9689
9690     const SMDSAbs_ElementType type = elem->GetType();
9691     const int                   id = elem->GetID();
9692     const int              nbNodes = elem->NbCornerNodes();
9693     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9694
9695     helper.SetSubShape( elem->getshapeId() );
9696
9697     if ( !smDS || !smDS->Contains( elem ))
9698       smDS = meshDS->MeshElements( elem->getshapeId() );
9699     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9700
9701     SMDS_MeshElement * newElem = 0;
9702     switch( nbNodes )
9703     {
9704     case 4: // cases for most frequently used element types go first (for optimization)
9705       if ( type == SMDSAbs_Volume )
9706         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9707       else
9708         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9709       break;
9710     case 8:
9711       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9712                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9713       break;
9714     case 3:
9715       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9716       break;
9717     case 2:
9718       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9719       break;
9720     case 5:
9721       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9722                                  nodes[4], id, theForce3d);
9723       break;
9724     case 6:
9725       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9726                                  nodes[4], nodes[5], id, theForce3d);
9727       break;
9728     default:;
9729     }
9730     ReplaceElemInGroups( elem, newElem, meshDS);
9731     if( newElem && smDS )
9732       smDS->AddElement( newElem );
9733
9734      // remove central nodes
9735     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9736       if ( nodes[i]->NbInverseElements() == 0 )
9737         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9738
9739   } // loop on theElements
9740
9741   if ( !theForce3d )
9742   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9743     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9744     // helper.FixQuadraticElements( myError );
9745     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9746   }
9747 }
9748
9749 //=======================================================================
9750 /*!
9751  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9752  * \return int - nb of checked elements
9753  */
9754 //=======================================================================
9755
9756 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9757                                      SMDS_ElemIteratorPtr theItr,
9758                                      const int            theShapeID)
9759 {
9760   int nbElem = 0;
9761   SMESHDS_Mesh* meshDS = GetMeshDS();
9762   ElemFeatures elemType;
9763   vector<const SMDS_MeshNode *> nodes;
9764
9765   while( theItr->more() )
9766   {
9767     const SMDS_MeshElement* elem = theItr->next();
9768     nbElem++;
9769     if( elem && elem->IsQuadratic())
9770     {
9771       // get elem data
9772       int nbCornerNodes = elem->NbCornerNodes();
9773       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9774
9775       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9776
9777       //remove a quadratic element
9778       if ( !theSm || !theSm->Contains( elem ))
9779         theSm = meshDS->MeshElements( elem->getshapeId() );
9780       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9781
9782       // remove medium nodes
9783       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9784         if ( nodes[i]->NbInverseElements() == 0 )
9785           meshDS->RemoveFreeNode( nodes[i], theSm );
9786
9787       // add a linear element
9788       nodes.resize( nbCornerNodes );
9789       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9790       ReplaceElemInGroups(elem, newElem, meshDS);
9791       if( theSm && newElem )
9792         theSm->AddElement( newElem );
9793     }
9794   }
9795   return nbElem;
9796 }
9797
9798 //=======================================================================
9799 //function : ConvertFromQuadratic
9800 //purpose  :
9801 //=======================================================================
9802
9803 bool SMESH_MeshEditor::ConvertFromQuadratic()
9804 {
9805   int nbCheckedElems = 0;
9806   if ( myMesh->HasShapeToMesh() )
9807   {
9808     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9809     {
9810       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9811       while ( smIt->more() ) {
9812         SMESH_subMesh* sm = smIt->next();
9813         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9814           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9815       }
9816     }
9817   }
9818
9819   int totalNbElems =
9820     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9821   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9822   {
9823     SMESHDS_SubMesh *aSM = 0;
9824     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9825   }
9826
9827   return true;
9828 }
9829
9830 namespace
9831 {
9832   //================================================================================
9833   /*!
9834    * \brief Return true if all medium nodes of the element are in the node set
9835    */
9836   //================================================================================
9837
9838   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9839   {
9840     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9841       if ( !nodeSet.count( elem->GetNode(i) ))
9842         return false;
9843     return true;
9844   }
9845 }
9846
9847 //================================================================================
9848 /*!
9849  * \brief Makes given elements linear
9850  */
9851 //================================================================================
9852
9853 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9854 {
9855   if ( theElements.empty() ) return;
9856
9857   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9858   set<int> mediumNodeIDs;
9859   TIDSortedElemSet::iterator eIt = theElements.begin();
9860   for ( ; eIt != theElements.end(); ++eIt )
9861   {
9862     const SMDS_MeshElement* e = *eIt;
9863     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9864       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9865   }
9866
9867   // replace given elements by linear ones
9868   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9869   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9870
9871   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9872   // except those elements sharing medium nodes of quadratic element whose medium nodes
9873   // are not all in mediumNodeIDs
9874
9875   // get remaining medium nodes
9876   TIDSortedNodeSet mediumNodes;
9877   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9878   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9879     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9880       mediumNodes.insert( mediumNodes.end(), n );
9881
9882   // find more quadratic elements to convert
9883   TIDSortedElemSet moreElemsToConvert;
9884   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9885   for ( ; nIt != mediumNodes.end(); ++nIt )
9886   {
9887     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9888     while ( invIt->more() )
9889     {
9890       const SMDS_MeshElement* e = invIt->next();
9891       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9892       {
9893         // find a more complex element including e and
9894         // whose medium nodes are not in mediumNodes
9895         bool complexFound = false;
9896         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9897         {
9898           SMDS_ElemIteratorPtr invIt2 =
9899             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9900           while ( invIt2->more() )
9901           {
9902             const SMDS_MeshElement* eComplex = invIt2->next();
9903             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9904             {
9905               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9906               if ( nbCommonNodes == e->NbNodes())
9907               {
9908                 complexFound = true;
9909                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9910                 break;
9911               }
9912             }
9913           }
9914         }
9915         if ( !complexFound )
9916           moreElemsToConvert.insert( e );
9917       }
9918     }
9919   }
9920   elemIt = elemSetIterator( moreElemsToConvert );
9921   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9922 }
9923
9924 //=======================================================================
9925 //function : SewSideElements
9926 //purpose  :
9927 //=======================================================================
9928
9929 SMESH_MeshEditor::Sew_Error
9930 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9931                                    TIDSortedElemSet&    theSide2,
9932                                    const SMDS_MeshNode* theFirstNode1,
9933                                    const SMDS_MeshNode* theFirstNode2,
9934                                    const SMDS_MeshNode* theSecondNode1,
9935                                    const SMDS_MeshNode* theSecondNode2)
9936 {
9937   myLastCreatedElems.Clear();
9938   myLastCreatedNodes.Clear();
9939
9940   if ( theSide1.size() != theSide2.size() )
9941     return SEW_DIFF_NB_OF_ELEMENTS;
9942
9943   Sew_Error aResult = SEW_OK;
9944   // Algo:
9945   // 1. Build set of faces representing each side
9946   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9947   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9948
9949   // =======================================================================
9950   // 1. Build set of faces representing each side:
9951   // =======================================================================
9952   // a. build set of nodes belonging to faces
9953   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9954   // c. create temporary faces representing side of volumes if correspondent
9955   //    face does not exist
9956
9957   SMESHDS_Mesh* aMesh = GetMeshDS();
9958   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9959   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9960   TIDSortedElemSet             faceSet1, faceSet2;
9961   set<const SMDS_MeshElement*> volSet1,  volSet2;
9962   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9963   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9964   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9965   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9966   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9967   int iSide, iFace, iNode;
9968
9969   list<const SMDS_MeshElement* > tempFaceList;
9970   for ( iSide = 0; iSide < 2; iSide++ ) {
9971     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9972     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9973     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9974     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9975     set<const SMDS_MeshElement*>::iterator vIt;
9976     TIDSortedElemSet::iterator eIt;
9977     set<const SMDS_MeshNode*>::iterator    nIt;
9978
9979     // check that given nodes belong to given elements
9980     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9981     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9982     int firstIndex = -1, secondIndex = -1;
9983     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9984       const SMDS_MeshElement* elem = *eIt;
9985       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9986       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9987       if ( firstIndex > -1 && secondIndex > -1 ) break;
9988     }
9989     if ( firstIndex < 0 || secondIndex < 0 ) {
9990       // we can simply return until temporary faces created
9991       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9992     }
9993
9994     // -----------------------------------------------------------
9995     // 1a. Collect nodes of existing faces
9996     //     and build set of face nodes in order to detect missing
9997     //     faces corresponding to sides of volumes
9998     // -----------------------------------------------------------
9999
10000     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10001
10002     // loop on the given element of a side
10003     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10004       //const SMDS_MeshElement* elem = *eIt;
10005       const SMDS_MeshElement* elem = *eIt;
10006       if ( elem->GetType() == SMDSAbs_Face ) {
10007         faceSet->insert( elem );
10008         set <const SMDS_MeshNode*> faceNodeSet;
10009         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10010         while ( nodeIt->more() ) {
10011           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10012           nodeSet->insert( n );
10013           faceNodeSet.insert( n );
10014         }
10015         setOfFaceNodeSet.insert( faceNodeSet );
10016       }
10017       else if ( elem->GetType() == SMDSAbs_Volume )
10018         volSet->insert( elem );
10019     }
10020     // ------------------------------------------------------------------------------
10021     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10022     // ------------------------------------------------------------------------------
10023
10024     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10025       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10026       while ( fIt->more() ) { // loop on faces sharing a node
10027         const SMDS_MeshElement* f = fIt->next();
10028         if ( faceSet->find( f ) == faceSet->end() ) {
10029           // check if all nodes are in nodeSet and
10030           // complete setOfFaceNodeSet if they are
10031           set <const SMDS_MeshNode*> faceNodeSet;
10032           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10033           bool allInSet = true;
10034           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10035             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10036             if ( nodeSet->find( n ) == nodeSet->end() )
10037               allInSet = false;
10038             else
10039               faceNodeSet.insert( n );
10040           }
10041           if ( allInSet ) {
10042             faceSet->insert( f );
10043             setOfFaceNodeSet.insert( faceNodeSet );
10044           }
10045         }
10046       }
10047     }
10048
10049     // -------------------------------------------------------------------------
10050     // 1c. Create temporary faces representing sides of volumes if correspondent
10051     //     face does not exist
10052     // -------------------------------------------------------------------------
10053
10054     if ( !volSet->empty() ) {
10055       //int nodeSetSize = nodeSet->size();
10056
10057       // loop on given volumes
10058       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10059         SMDS_VolumeTool vol (*vIt);
10060         // loop on volume faces: find free faces
10061         // --------------------------------------
10062         list<const SMDS_MeshElement* > freeFaceList;
10063         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10064           if ( !vol.IsFreeFace( iFace ))
10065             continue;
10066           // check if there is already a face with same nodes in a face set
10067           const SMDS_MeshElement* aFreeFace = 0;
10068           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10069           int nbNodes = vol.NbFaceNodes( iFace );
10070           set <const SMDS_MeshNode*> faceNodeSet;
10071           vol.GetFaceNodes( iFace, faceNodeSet );
10072           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10073           if ( isNewFace ) {
10074             // no such a face is given but it still can exist, check it
10075             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10076             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10077           }
10078           if ( !aFreeFace ) {
10079             // create a temporary face
10080             if ( nbNodes == 3 ) {
10081               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10082               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10083             }
10084             else if ( nbNodes == 4 ) {
10085               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10086               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10087             }
10088             else {
10089               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10090               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10091               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10092             }
10093             if ( aFreeFace )
10094               tempFaceList.push_back( aFreeFace );
10095           }
10096
10097           if ( aFreeFace )
10098             freeFaceList.push_back( aFreeFace );
10099
10100         } // loop on faces of a volume
10101
10102         // choose one of several free faces of a volume
10103         // --------------------------------------------
10104         if ( freeFaceList.size() > 1 ) {
10105           // choose a face having max nb of nodes shared by other elems of a side
10106           int maxNbNodes = -1;
10107           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10108           while ( fIt != freeFaceList.end() ) { // loop on free faces
10109             int nbSharedNodes = 0;
10110             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10111             while ( nodeIt->more() ) { // loop on free face nodes
10112               const SMDS_MeshNode* n =
10113                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10114               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10115               while ( invElemIt->more() ) {
10116                 const SMDS_MeshElement* e = invElemIt->next();
10117                 nbSharedNodes += faceSet->count( e );
10118                 nbSharedNodes += elemSet->count( e );
10119               }
10120             }
10121             if ( nbSharedNodes > maxNbNodes ) {
10122               maxNbNodes = nbSharedNodes;
10123               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10124             }
10125             else if ( nbSharedNodes == maxNbNodes ) {
10126               fIt++;
10127             }
10128             else {
10129               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10130             }
10131           }
10132           if ( freeFaceList.size() > 1 )
10133           {
10134             // could not choose one face, use another way
10135             // choose a face most close to the bary center of the opposite side
10136             gp_XYZ aBC( 0., 0., 0. );
10137             set <const SMDS_MeshNode*> addedNodes;
10138             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10139             eIt = elemSet2->begin();
10140             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10141               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10142               while ( nodeIt->more() ) { // loop on free face nodes
10143                 const SMDS_MeshNode* n =
10144                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10145                 if ( addedNodes.insert( n ).second )
10146                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10147               }
10148             }
10149             aBC /= addedNodes.size();
10150             double minDist = DBL_MAX;
10151             fIt = freeFaceList.begin();
10152             while ( fIt != freeFaceList.end() ) { // loop on free faces
10153               double dist = 0;
10154               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10155               while ( nodeIt->more() ) { // loop on free face nodes
10156                 const SMDS_MeshNode* n =
10157                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10158                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10159                 dist += ( aBC - p ).SquareModulus();
10160               }
10161               if ( dist < minDist ) {
10162                 minDist = dist;
10163                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10164               }
10165               else
10166                 fIt = freeFaceList.erase( fIt++ );
10167             }
10168           }
10169         } // choose one of several free faces of a volume
10170
10171         if ( freeFaceList.size() == 1 ) {
10172           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10173           faceSet->insert( aFreeFace );
10174           // complete a node set with nodes of a found free face
10175           //           for ( iNode = 0; iNode < ; iNode++ )
10176           //             nodeSet->insert( fNodes[ iNode ] );
10177         }
10178
10179       } // loop on volumes of a side
10180
10181       //       // complete a set of faces if new nodes in a nodeSet appeared
10182       //       // ----------------------------------------------------------
10183       //       if ( nodeSetSize != nodeSet->size() ) {
10184       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10185       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10186       //           while ( fIt->more() ) { // loop on faces sharing a node
10187       //             const SMDS_MeshElement* f = fIt->next();
10188       //             if ( faceSet->find( f ) == faceSet->end() ) {
10189       //               // check if all nodes are in nodeSet and
10190       //               // complete setOfFaceNodeSet if they are
10191       //               set <const SMDS_MeshNode*> faceNodeSet;
10192       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10193       //               bool allInSet = true;
10194       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10195       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10196       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10197       //                   allInSet = false;
10198       //                 else
10199       //                   faceNodeSet.insert( n );
10200       //               }
10201       //               if ( allInSet ) {
10202       //                 faceSet->insert( f );
10203       //                 setOfFaceNodeSet.insert( faceNodeSet );
10204       //               }
10205       //             }
10206       //           }
10207       //         }
10208       //       }
10209     } // Create temporary faces, if there are volumes given
10210   } // loop on sides
10211
10212   if ( faceSet1.size() != faceSet2.size() ) {
10213     // delete temporary faces: they are in reverseElements of actual nodes
10214 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10215 //    while ( tmpFaceIt->more() )
10216 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10217 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10218 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10219 //      aMesh->RemoveElement(*tmpFaceIt);
10220     MESSAGE("Diff nb of faces");
10221     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10222   }
10223
10224   // ============================================================
10225   // 2. Find nodes to merge:
10226   //              bind a node to remove to a node to put instead
10227   // ============================================================
10228
10229   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10230   if ( theFirstNode1 != theFirstNode2 )
10231     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10232   if ( theSecondNode1 != theSecondNode2 )
10233     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10234
10235   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10236   set< long > linkIdSet; // links to process
10237   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10238
10239   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10240   list< NLink > linkList[2];
10241   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10242   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10243   // loop on links in linkList; find faces by links and append links
10244   // of the found faces to linkList
10245   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10246   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10247   {
10248     NLink link[] = { *linkIt[0], *linkIt[1] };
10249     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10250     if ( !linkIdSet.count( linkID ) )
10251       continue;
10252
10253     // by links, find faces in the face sets,
10254     // and find indices of link nodes in the found faces;
10255     // in a face set, there is only one or no face sharing a link
10256     // ---------------------------------------------------------------
10257
10258     const SMDS_MeshElement* face[] = { 0, 0 };
10259     vector<const SMDS_MeshNode*> fnodes[2];
10260     int iLinkNode[2][2];
10261     TIDSortedElemSet avoidSet;
10262     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10263       const SMDS_MeshNode* n1 = link[iSide].first;
10264       const SMDS_MeshNode* n2 = link[iSide].second;
10265       //cout << "Side " << iSide << " ";
10266       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10267       // find a face by two link nodes
10268       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10269                                                       *faceSetPtr[ iSide ], avoidSet,
10270                                                       &iLinkNode[iSide][0],
10271                                                       &iLinkNode[iSide][1] );
10272       if ( face[ iSide ])
10273       {
10274         //cout << " F " << face[ iSide]->GetID() <<endl;
10275         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10276         // put face nodes to fnodes
10277         if ( face[ iSide ]->IsQuadratic() )
10278         {
10279           // use interlaced nodes iterator
10280           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10281           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10282           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10283           while ( nIter->more() )
10284             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10285         }
10286         else
10287         {
10288           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10289                                   face[ iSide ]->end_nodes() );
10290         }
10291         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10292       }
10293     }
10294
10295     // check similarity of elements of the sides
10296     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10297       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10298       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10299         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10300       }
10301       else {
10302         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10303       }
10304       break; // do not return because it's necessary to remove tmp faces
10305     }
10306
10307     // set nodes to merge
10308     // -------------------
10309
10310     if ( face[0] && face[1] )  {
10311       const int nbNodes = face[0]->NbNodes();
10312       if ( nbNodes != face[1]->NbNodes() ) {
10313         MESSAGE("Diff nb of face nodes");
10314         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10315         break; // do not return because it s necessary to remove tmp faces
10316       }
10317       bool reverse[] = { false, false }; // order of nodes in the link
10318       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10319         // analyse link orientation in faces
10320         int i1 = iLinkNode[ iSide ][ 0 ];
10321         int i2 = iLinkNode[ iSide ][ 1 ];
10322         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10323       }
10324       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10325       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10326       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10327       {
10328         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10329                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10330       }
10331
10332       // add other links of the faces to linkList
10333       // -----------------------------------------
10334
10335       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10336         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10337         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10338         if ( !iter_isnew.second ) { // already in a set: no need to process
10339           linkIdSet.erase( iter_isnew.first );
10340         }
10341         else // new in set == encountered for the first time: add
10342         {
10343           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10344           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10345           linkList[0].push_back ( NLink( n1, n2 ));
10346           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10347         }
10348       }
10349     } // 2 faces found
10350
10351     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10352       break;
10353
10354   } // loop on link lists
10355
10356   if ( aResult == SEW_OK &&
10357        ( //linkIt[0] != linkList[0].end() ||
10358          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10359     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10360              " " << (faceSetPtr[1]->empty()));
10361     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10362   }
10363
10364   // ====================================================================
10365   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10366   // ====================================================================
10367
10368   // delete temporary faces
10369 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10370 //  while ( tmpFaceIt->more() )
10371 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10372   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10373   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10374     aMesh->RemoveElement(*tmpFaceIt);
10375
10376   if ( aResult != SEW_OK)
10377     return aResult;
10378
10379   list< int > nodeIDsToRemove;
10380   vector< const SMDS_MeshNode*> nodes;
10381   ElemFeatures elemType;
10382
10383   // loop on nodes replacement map
10384   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10385   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10386     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10387     {
10388       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10389       nodeIDsToRemove.push_back( nToRemove->GetID() );
10390       // loop on elements sharing nToRemove
10391       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10392       while ( invElemIt->more() ) {
10393         const SMDS_MeshElement* e = invElemIt->next();
10394         // get a new suite of nodes: make replacement
10395         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10396         nodes.resize( nbNodes );
10397         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10398         while ( nIt->more() ) {
10399           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10400           nnIt = nReplaceMap.find( n );
10401           if ( nnIt != nReplaceMap.end() ) {
10402             nbReplaced++;
10403             n = (*nnIt).second;
10404           }
10405           nodes[ i++ ] = n;
10406         }
10407         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10408         //         elemIDsToRemove.push_back( e->GetID() );
10409         //       else
10410         if ( nbReplaced )
10411         {
10412           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10413           aMesh->RemoveElement( e );
10414
10415           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10416           {
10417             AddToSameGroups( newElem, e, aMesh );
10418             if ( int aShapeId = e->getshapeId() )
10419               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10420           }
10421         }
10422       }
10423     }
10424
10425   Remove( nodeIDsToRemove, true );
10426
10427   return aResult;
10428 }
10429
10430 //================================================================================
10431 /*!
10432  * \brief Find corresponding nodes in two sets of faces
10433  * \param theSide1 - first face set
10434  * \param theSide2 - second first face
10435  * \param theFirstNode1 - a boundary node of set 1
10436  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10437  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10438  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10439  * \param nReplaceMap - output map of corresponding nodes
10440  * \return bool  - is a success or not
10441  */
10442 //================================================================================
10443
10444 #ifdef _DEBUG_
10445 //#define DEBUG_MATCHING_NODES
10446 #endif
10447
10448 SMESH_MeshEditor::Sew_Error
10449 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10450                                     set<const SMDS_MeshElement*>& theSide2,
10451                                     const SMDS_MeshNode*          theFirstNode1,
10452                                     const SMDS_MeshNode*          theFirstNode2,
10453                                     const SMDS_MeshNode*          theSecondNode1,
10454                                     const SMDS_MeshNode*          theSecondNode2,
10455                                     TNodeNodeMap &                nReplaceMap)
10456 {
10457   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10458
10459   nReplaceMap.clear();
10460   if ( theFirstNode1 != theFirstNode2 )
10461     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10462   if ( theSecondNode1 != theSecondNode2 )
10463     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10464
10465   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10466   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10467
10468   list< NLink > linkList[2];
10469   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10470   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10471
10472   // loop on links in linkList; find faces by links and append links
10473   // of the found faces to linkList
10474   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10475   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10476     NLink link[] = { *linkIt[0], *linkIt[1] };
10477     if ( linkSet.find( link[0] ) == linkSet.end() )
10478       continue;
10479
10480     // by links, find faces in the face sets,
10481     // and find indices of link nodes in the found faces;
10482     // in a face set, there is only one or no face sharing a link
10483     // ---------------------------------------------------------------
10484
10485     const SMDS_MeshElement* face[] = { 0, 0 };
10486     list<const SMDS_MeshNode*> notLinkNodes[2];
10487     //bool reverse[] = { false, false }; // order of notLinkNodes
10488     int nbNodes[2];
10489     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10490     {
10491       const SMDS_MeshNode* n1 = link[iSide].first;
10492       const SMDS_MeshNode* n2 = link[iSide].second;
10493       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10494       set< const SMDS_MeshElement* > facesOfNode1;
10495       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10496       {
10497         // during a loop of the first node, we find all faces around n1,
10498         // during a loop of the second node, we find one face sharing both n1 and n2
10499         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10500         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10501         while ( fIt->more() ) { // loop on faces sharing a node
10502           const SMDS_MeshElement* f = fIt->next();
10503           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10504               ! facesOfNode1.insert( f ).second ) // f encounters twice
10505           {
10506             if ( face[ iSide ] ) {
10507               MESSAGE( "2 faces per link " );
10508               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10509             }
10510             face[ iSide ] = f;
10511             faceSet->erase( f );
10512
10513             // get not link nodes
10514             int nbN = f->NbNodes();
10515             if ( f->IsQuadratic() )
10516               nbN /= 2;
10517             nbNodes[ iSide ] = nbN;
10518             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10519             int i1 = f->GetNodeIndex( n1 );
10520             int i2 = f->GetNodeIndex( n2 );
10521             int iEnd = nbN, iBeg = -1, iDelta = 1;
10522             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10523             if ( reverse ) {
10524               std::swap( iEnd, iBeg ); iDelta = -1;
10525             }
10526             int i = i2;
10527             while ( true ) {
10528               i += iDelta;
10529               if ( i == iEnd ) i = iBeg + iDelta;
10530               if ( i == i1 ) break;
10531               nodes.push_back ( f->GetNode( i ) );
10532             }
10533           }
10534         }
10535       }
10536     }
10537     // check similarity of elements of the sides
10538     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10539       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10540       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10541         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10542       }
10543       else {
10544         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10545       }
10546     }
10547
10548     // set nodes to merge
10549     // -------------------
10550
10551     if ( face[0] && face[1] )  {
10552       if ( nbNodes[0] != nbNodes[1] ) {
10553         MESSAGE("Diff nb of face nodes");
10554         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10555       }
10556 #ifdef DEBUG_MATCHING_NODES
10557       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10558                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10559                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10560 #endif
10561       int nbN = nbNodes[0];
10562       {
10563         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10564         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10565         for ( int i = 0 ; i < nbN - 2; ++i ) {
10566 #ifdef DEBUG_MATCHING_NODES
10567           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10568 #endif
10569           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10570         }
10571       }
10572
10573       // add other links of the face 1 to linkList
10574       // -----------------------------------------
10575
10576       const SMDS_MeshElement* f0 = face[0];
10577       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10578       for ( int i = 0; i < nbN; i++ )
10579       {
10580         const SMDS_MeshNode* n2 = f0->GetNode( i );
10581         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10582           linkSet.insert( SMESH_TLink( n1, n2 ));
10583         if ( !iter_isnew.second ) { // already in a set: no need to process
10584           linkSet.erase( iter_isnew.first );
10585         }
10586         else // new in set == encountered for the first time: add
10587         {
10588 #ifdef DEBUG_MATCHING_NODES
10589           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10590                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10591 #endif
10592           linkList[0].push_back ( NLink( n1, n2 ));
10593           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10594         }
10595         n1 = n2;
10596       }
10597     } // 2 faces found
10598   } // loop on link lists
10599
10600   return SEW_OK;
10601 }
10602
10603 //================================================================================
10604 /*!
10605  * \brief Create elements equal (on same nodes) to given ones
10606  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10607  *              elements of the uppest dimension are duplicated.
10608  */
10609 //================================================================================
10610
10611 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10612 {
10613   ClearLastCreated();
10614   SMESHDS_Mesh* mesh = GetMeshDS();
10615
10616   // get an element type and an iterator over elements
10617
10618   SMDSAbs_ElementType type = SMDSAbs_All;
10619   SMDS_ElemIteratorPtr elemIt;
10620   vector< const SMDS_MeshElement* > allElems;
10621   if ( theElements.empty() )
10622   {
10623     if ( mesh->NbNodes() == 0 )
10624       return;
10625     // get most complex type
10626     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10627       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10628       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10629     };
10630     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10631       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10632       {
10633         type = types[i];
10634         break;
10635       }
10636     // put all elements in the vector <allElems>
10637     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10638     elemIt = mesh->elementsIterator( type );
10639     while ( elemIt->more() )
10640       allElems.push_back( elemIt->next());
10641     elemIt = elemSetIterator( allElems );
10642   }
10643   else
10644   {
10645     type = (*theElements.begin())->GetType();
10646     elemIt = elemSetIterator( theElements );
10647   }
10648
10649   // duplicate elements
10650
10651   ElemFeatures elemType;
10652
10653   vector< const SMDS_MeshNode* > nodes;
10654   while ( elemIt->more() )
10655   {
10656     const SMDS_MeshElement* elem = elemIt->next();
10657     if ( elem->GetType() != type )
10658       continue;
10659
10660     elemType.Init( elem, /*basicOnly=*/false );
10661     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10662
10663     AddElement( nodes, elemType );
10664   }
10665 }
10666
10667 //================================================================================
10668 /*!
10669   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10670   \param theElems - the list of elements (edges or faces) to be replicated
10671   The nodes for duplication could be found from these elements
10672   \param theNodesNot - list of nodes to NOT replicate
10673   \param theAffectedElems - the list of elements (cells and edges) to which the
10674   replicated nodes should be associated to.
10675   \return TRUE if operation has been completed successfully, FALSE otherwise
10676 */
10677 //================================================================================
10678
10679 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10680                                     const TIDSortedElemSet& theNodesNot,
10681                                     const TIDSortedElemSet& theAffectedElems )
10682 {
10683   myLastCreatedElems.Clear();
10684   myLastCreatedNodes.Clear();
10685
10686   if ( theElems.size() == 0 )
10687     return false;
10688
10689   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10690   if ( !aMeshDS )
10691     return false;
10692
10693   bool res = false;
10694   TNodeNodeMap anOldNodeToNewNode;
10695   // duplicate elements and nodes
10696   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10697   // replce nodes by duplications
10698   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10699   return res;
10700 }
10701
10702 //================================================================================
10703 /*!
10704   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10705   \param theMeshDS - mesh instance
10706   \param theElems - the elements replicated or modified (nodes should be changed)
10707   \param theNodesNot - nodes to NOT replicate
10708   \param theNodeNodeMap - relation of old node to new created node
10709   \param theIsDoubleElem - flag os to replicate element or modify
10710   \return TRUE if operation has been completed successfully, FALSE otherwise
10711 */
10712 //================================================================================
10713
10714 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10715                                    const TIDSortedElemSet& theElems,
10716                                    const TIDSortedElemSet& theNodesNot,
10717                                    TNodeNodeMap&           theNodeNodeMap,
10718                                    const bool              theIsDoubleElem )
10719 {
10720   // iterate through element and duplicate them (by nodes duplication)
10721   bool res = false;
10722   std::vector<const SMDS_MeshNode*> newNodes;
10723   ElemFeatures elemType;
10724
10725   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10726   for ( ;  elemItr != theElems.end(); ++elemItr )
10727   {
10728     const SMDS_MeshElement* anElem = *elemItr;
10729     if (!anElem)
10730       continue;
10731
10732     // duplicate nodes to duplicate element
10733     bool isDuplicate = false;
10734     newNodes.resize( anElem->NbNodes() );
10735     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10736     int ind = 0;
10737     while ( anIter->more() )
10738     {
10739       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10740       const SMDS_MeshNode*  aNewNode = aCurrNode;
10741       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10742       if ( n2n != theNodeNodeMap.end() )
10743       {
10744         aNewNode = n2n->second;
10745       }
10746       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10747       {
10748         // duplicate node
10749         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10750         copyPosition( aCurrNode, aNewNode );
10751         theNodeNodeMap[ aCurrNode ] = aNewNode;
10752         myLastCreatedNodes.Append( aNewNode );
10753       }
10754       isDuplicate |= (aCurrNode != aNewNode);
10755       newNodes[ ind++ ] = aNewNode;
10756     }
10757     if ( !isDuplicate )
10758       continue;
10759
10760     if ( theIsDoubleElem )
10761       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10762     else
10763       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10764
10765     res = true;
10766   }
10767   return res;
10768 }
10769
10770 //================================================================================
10771 /*!
10772   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10773   \param theNodes - identifiers of nodes to be doubled
10774   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10775   nodes. If list of element identifiers is empty then nodes are doubled but
10776   they not assigned to elements
10777   \return TRUE if operation has been completed successfully, FALSE otherwise
10778 */
10779 //================================================================================
10780
10781 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10782                                     const std::list< int >& theListOfModifiedElems )
10783 {
10784   myLastCreatedElems.Clear();
10785   myLastCreatedNodes.Clear();
10786
10787   if ( theListOfNodes.size() == 0 )
10788     return false;
10789
10790   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10791   if ( !aMeshDS )
10792     return false;
10793
10794   // iterate through nodes and duplicate them
10795
10796   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10797
10798   std::list< int >::const_iterator aNodeIter;
10799   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10800   {
10801     int aCurr = *aNodeIter;
10802     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10803     if ( !aNode )
10804       continue;
10805
10806     // duplicate node
10807
10808     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10809     if ( aNewNode )
10810     {
10811       copyPosition( aNode, aNewNode );
10812       anOldNodeToNewNode[ aNode ] = aNewNode;
10813       myLastCreatedNodes.Append( aNewNode );
10814     }
10815   }
10816
10817   // Create map of new nodes for modified elements
10818
10819   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10820
10821   std::list< int >::const_iterator anElemIter;
10822   for ( anElemIter = theListOfModifiedElems.begin();
10823         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10824   {
10825     int aCurr = *anElemIter;
10826     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10827     if ( !anElem )
10828       continue;
10829
10830     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10831
10832     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10833     int ind = 0;
10834     while ( anIter->more() )
10835     {
10836       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10837       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10838       {
10839         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10840         aNodeArr[ ind++ ] = aNewNode;
10841       }
10842       else
10843         aNodeArr[ ind++ ] = aCurrNode;
10844     }
10845     anElemToNodes[ anElem ] = aNodeArr;
10846   }
10847
10848   // Change nodes of elements
10849
10850   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10851     anElemToNodesIter = anElemToNodes.begin();
10852   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10853   {
10854     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10855     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10856     if ( anElem )
10857     {
10858       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10859     }
10860   }
10861
10862   return true;
10863 }
10864
10865 namespace {
10866
10867   //================================================================================
10868   /*!
10869   \brief Check if element located inside shape
10870   \return TRUE if IN or ON shape, FALSE otherwise
10871   */
10872   //================================================================================
10873
10874   template<class Classifier>
10875   bool isInside(const SMDS_MeshElement* theElem,
10876                 Classifier&             theClassifier,
10877                 const double            theTol)
10878   {
10879     gp_XYZ centerXYZ (0, 0, 0);
10880     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10881     while (aNodeItr->more())
10882       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10883
10884     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10885     theClassifier.Perform(aPnt, theTol);
10886     TopAbs_State aState = theClassifier.State();
10887     return (aState == TopAbs_IN || aState == TopAbs_ON );
10888   }
10889
10890   //================================================================================
10891   /*!
10892    * \brief Classifier of the 3D point on the TopoDS_Face
10893    *        with interaface suitable for isInside()
10894    */
10895   //================================================================================
10896
10897   struct _FaceClassifier
10898   {
10899     Extrema_ExtPS       _extremum;
10900     BRepAdaptor_Surface _surface;
10901     TopAbs_State        _state;
10902
10903     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10904     {
10905       _extremum.Initialize( _surface,
10906                             _surface.FirstUParameter(), _surface.LastUParameter(),
10907                             _surface.FirstVParameter(), _surface.LastVParameter(),
10908                             _surface.Tolerance(), _surface.Tolerance() );
10909     }
10910     void Perform(const gp_Pnt& aPnt, double theTol)
10911     {
10912       theTol *= theTol;
10913       _state = TopAbs_OUT;
10914       _extremum.Perform(aPnt);
10915       if ( _extremum.IsDone() )
10916         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10917           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10918     }
10919     TopAbs_State State() const
10920     {
10921       return _state;
10922     }
10923   };
10924 }
10925
10926 //================================================================================
10927 /*!
10928   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10929   This method is the first step of DoubleNodeElemGroupsInRegion.
10930   \param theElems - list of groups of elements (edges or faces) to be replicated
10931   \param theNodesNot - list of groups of nodes not to replicated
10932   \param theShape - shape to detect affected elements (element which geometric center
10933          located on or inside shape). If the shape is null, detection is done on faces orientations
10934          (select elements with a gravity center on the side given by faces normals).
10935          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10936          The replicated nodes should be associated to affected elements.
10937   \return groups of affected elements
10938   \sa DoubleNodeElemGroupsInRegion()
10939  */
10940 //================================================================================
10941
10942 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10943                                                    const TIDSortedElemSet& theNodesNot,
10944                                                    const TopoDS_Shape&     theShape,
10945                                                    TIDSortedElemSet&       theAffectedElems)
10946 {
10947   if ( theShape.IsNull() )
10948   {
10949     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10950     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10951     std::set<const SMDS_MeshElement*> edgesToCheck;
10952     alreadyCheckedNodes.clear();
10953     alreadyCheckedElems.clear();
10954     edgesToCheck.clear();
10955
10956     // --- iterates on elements to be replicated and get elements by back references from their nodes
10957
10958     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10959     for ( ;  elemItr != theElems.end(); ++elemItr )
10960     {
10961       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10962       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10963         continue;
10964       gp_XYZ normal;
10965       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10966       std::set<const SMDS_MeshNode*> nodesElem;
10967       nodesElem.clear();
10968       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10969       while ( nodeItr->more() )
10970       {
10971         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10972         nodesElem.insert(aNode);
10973       }
10974       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10975       for (; nodit != nodesElem.end(); nodit++)
10976       {
10977         const SMDS_MeshNode* aNode = *nodit;
10978         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10979           continue;
10980         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10981           continue;
10982         alreadyCheckedNodes.insert(aNode);
10983         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10984         while ( backElemItr->more() )
10985         {
10986           const SMDS_MeshElement* curElem = backElemItr->next();
10987           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10988             continue;
10989           if (theElems.find(curElem) != theElems.end())
10990             continue;
10991           alreadyCheckedElems.insert(curElem);
10992           double x=0, y=0, z=0;
10993           int nb = 0;
10994           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10995           while ( nodeItr2->more() )
10996           {
10997             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10998             x += anotherNode->X();
10999             y += anotherNode->Y();
11000             z += anotherNode->Z();
11001             nb++;
11002           }
11003           gp_XYZ p;
11004           p.SetCoord( x/nb -aNode->X(),
11005                       y/nb -aNode->Y(),
11006                       z/nb -aNode->Z() );
11007           if (normal*p > 0)
11008           {
11009             theAffectedElems.insert( curElem );
11010           }
11011           else if (curElem->GetType() == SMDSAbs_Edge)
11012             edgesToCheck.insert(curElem);
11013         }
11014       }
11015     }
11016     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11017     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11018     for( ; eit != edgesToCheck.end(); eit++)
11019     {
11020       bool onside = true;
11021       const SMDS_MeshElement* anEdge = *eit;
11022       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11023       while ( nodeItr->more() )
11024       {
11025         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11026         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11027         {
11028           onside = false;
11029           break;
11030         }
11031       }
11032       if (onside)
11033       {
11034         theAffectedElems.insert(anEdge);
11035       }
11036     }
11037   }
11038   else
11039   {
11040     const double aTol = Precision::Confusion();
11041     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11042     auto_ptr<_FaceClassifier>              aFaceClassifier;
11043     if ( theShape.ShapeType() == TopAbs_SOLID )
11044     {
11045       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11046       bsc3d->PerformInfinitePoint(aTol);
11047     }
11048     else if (theShape.ShapeType() == TopAbs_FACE )
11049     {
11050       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11051     }
11052
11053     // iterates on indicated elements and get elements by back references from their nodes
11054     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11055     for ( ;  elemItr != theElems.end(); ++elemItr )
11056     {
11057       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11058       if (!anElem)
11059         continue;
11060       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11061       while ( nodeItr->more() )
11062       {
11063         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11064         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11065           continue;
11066         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11067         while ( backElemItr->more() )
11068         {
11069           const SMDS_MeshElement* curElem = backElemItr->next();
11070           if ( curElem && theElems.find(curElem) == theElems.end() &&
11071               ( bsc3d.get() ?
11072                 isInside( curElem, *bsc3d, aTol ) :
11073                 isInside( curElem, *aFaceClassifier, aTol )))
11074             theAffectedElems.insert( curElem );
11075         }
11076       }
11077     }
11078   }
11079   return true;
11080 }
11081
11082 //================================================================================
11083 /*!
11084   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11085   \param theElems - group of of elements (edges or faces) to be replicated
11086   \param theNodesNot - group of nodes not to replicate
11087   \param theShape - shape to detect affected elements (element which geometric center
11088   located on or inside shape).
11089   The replicated nodes should be associated to affected elements.
11090   \return TRUE if operation has been completed successfully, FALSE otherwise
11091 */
11092 //================================================================================
11093
11094 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11095                                             const TIDSortedElemSet& theNodesNot,
11096                                             const TopoDS_Shape&     theShape )
11097 {
11098   if ( theShape.IsNull() )
11099     return false;
11100
11101   const double aTol = Precision::Confusion();
11102   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11103   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11104   if ( theShape.ShapeType() == TopAbs_SOLID )
11105   {
11106     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11107     bsc3d->PerformInfinitePoint(aTol);
11108   }
11109   else if (theShape.ShapeType() == TopAbs_FACE )
11110   {
11111     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11112   }
11113
11114   // iterates on indicated elements and get elements by back references from their nodes
11115   TIDSortedElemSet anAffected;
11116   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11117   for ( ;  elemItr != theElems.end(); ++elemItr )
11118   {
11119     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11120     if (!anElem)
11121       continue;
11122
11123     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11124     while ( nodeItr->more() )
11125     {
11126       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11127       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11128         continue;
11129       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11130       while ( backElemItr->more() )
11131       {
11132         const SMDS_MeshElement* curElem = backElemItr->next();
11133         if ( curElem && theElems.find(curElem) == theElems.end() &&
11134              ( bsc3d ?
11135                isInside( curElem, *bsc3d, aTol ) :
11136                isInside( curElem, *aFaceClassifier, aTol )))
11137           anAffected.insert( curElem );
11138       }
11139     }
11140   }
11141   return DoubleNodes( theElems, theNodesNot, anAffected );
11142 }
11143
11144 /*!
11145  *  \brief compute an oriented angle between two planes defined by four points.
11146  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11147  *  @param p0 base of the rotation axe
11148  *  @param p1 extremity of the rotation axe
11149  *  @param g1 belongs to the first plane
11150  *  @param g2 belongs to the second plane
11151  */
11152 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11153 {
11154   gp_Vec vref(p0, p1);
11155   gp_Vec v1(p0, g1);
11156   gp_Vec v2(p0, g2);
11157   gp_Vec n1 = vref.Crossed(v1);
11158   gp_Vec n2 = vref.Crossed(v2);
11159   try {
11160     return n2.AngleWithRef(n1, vref);
11161   }
11162   catch ( Standard_Failure ) {
11163   }
11164   return Max( v1.Magnitude(), v2.Magnitude() );
11165 }
11166
11167 /*!
11168  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11169  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11170  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11171  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11172  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11173  * 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.
11174  * 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.
11175  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11176  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11177  * \param theElems - list of groups of volumes, where a group of volume is a set of
11178  *        SMDS_MeshElements sorted by Id.
11179  * \param createJointElems - if TRUE, create the elements
11180  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11181  *        the boundary between \a theDomains and the rest mesh
11182  * \return TRUE if operation has been completed successfully, FALSE otherwise
11183  */
11184 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11185                                                      bool                                 createJointElems,
11186                                                      bool                                 onAllBoundaries)
11187 {
11188   // MESSAGE("----------------------------------------------");
11189   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11190   // MESSAGE("----------------------------------------------");
11191
11192   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11193   meshDS->BuildDownWardConnectivity(true);
11194   CHRONO(50);
11195   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11196
11197   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11198   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11199   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11200
11201   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11202   std::map<int,int>celldom; // cell vtkId --> domain
11203   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11204   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11205   faceDomains.clear();
11206   celldom.clear();
11207   cellDomains.clear();
11208   nodeDomains.clear();
11209   std::map<int,int> emptyMap;
11210   std::set<int> emptySet;
11211   emptyMap.clear();
11212
11213   //MESSAGE(".. Number of domains :"<<theElems.size());
11214
11215   TIDSortedElemSet theRestDomElems;
11216   const int iRestDom  = -1;
11217   const int idom0     = onAllBoundaries ? iRestDom : 0;
11218   const int nbDomains = theElems.size();
11219
11220   // Check if the domains do not share an element
11221   for (int idom = 0; idom < nbDomains-1; idom++)
11222   {
11223     //       MESSAGE("... Check of domain #" << idom);
11224     const TIDSortedElemSet& domain = theElems[idom];
11225     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11226     for (; elemItr != domain.end(); ++elemItr)
11227     {
11228       const SMDS_MeshElement* anElem = *elemItr;
11229       int idombisdeb = idom + 1 ;
11230       // check if the element belongs to a domain further in the list
11231       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11232       {
11233         const TIDSortedElemSet& domainbis = theElems[idombis];
11234         if ( domainbis.count( anElem ))
11235         {
11236           MESSAGE(".... Domain #" << idom);
11237           MESSAGE(".... Domain #" << idombis);
11238           throw SALOME_Exception("The domains are not disjoint.");
11239           return false ;
11240         }
11241       }
11242     }
11243   }
11244
11245   for (int idom = 0; idom < nbDomains; idom++)
11246   {
11247
11248     // --- build a map (face to duplicate --> volume to modify)
11249     //     with all the faces shared by 2 domains (group of elements)
11250     //     and corresponding volume of this domain, for each shared face.
11251     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11252
11253     //MESSAGE("... Neighbors of domain #" << idom);
11254     const TIDSortedElemSet& domain = theElems[idom];
11255     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11256     for (; elemItr != domain.end(); ++elemItr)
11257     {
11258       const SMDS_MeshElement* anElem = *elemItr;
11259       if (!anElem)
11260         continue;
11261       int vtkId = anElem->getVtkId();
11262       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11263       int neighborsVtkIds[NBMAXNEIGHBORS];
11264       int downIds[NBMAXNEIGHBORS];
11265       unsigned char downTypes[NBMAXNEIGHBORS];
11266       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11267       for (int n = 0; n < nbNeighbors; n++)
11268       {
11269         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11270         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11271         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11272         {
11273           bool ok = false;
11274           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11275           {
11276             // MESSAGE("Domain " << idombis);
11277             const TIDSortedElemSet& domainbis = theElems[idombis];
11278             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11279           }
11280           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11281           {
11282             DownIdType face(downIds[n], downTypes[n]);
11283             if (!faceDomains[face].count(idom))
11284             {
11285               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11286               celldom[vtkId] = idom;
11287               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11288             }
11289             if ( !ok )
11290             {
11291               theRestDomElems.insert( elem );
11292               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11293               celldom[neighborsVtkIds[n]] = iRestDom;
11294             }
11295           }
11296         }
11297       }
11298     }
11299   }
11300
11301   //MESSAGE("Number of shared faces " << faceDomains.size());
11302   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11303
11304   // --- explore the shared faces domain by domain,
11305   //     explore the nodes of the face and see if they belong to a cell in the domain,
11306   //     which has only a node or an edge on the border (not a shared face)
11307
11308   for (int idomain = idom0; idomain < nbDomains; idomain++)
11309   {
11310     //MESSAGE("Domain " << idomain);
11311     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11312     itface = faceDomains.begin();
11313     for (; itface != faceDomains.end(); ++itface)
11314     {
11315       const std::map<int, int>& domvol = itface->second;
11316       if (!domvol.count(idomain))
11317         continue;
11318       DownIdType face = itface->first;
11319       //MESSAGE(" --- face " << face.cellId);
11320       std::set<int> oldNodes;
11321       oldNodes.clear();
11322       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11323       std::set<int>::iterator itn = oldNodes.begin();
11324       for (; itn != oldNodes.end(); ++itn)
11325       {
11326         int oldId = *itn;
11327         //MESSAGE("     node " << oldId);
11328         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11329         for (int i=0; i<l.ncells; i++)
11330         {
11331           int vtkId = l.cells[i];
11332           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11333           if (!domain.count(anElem))
11334             continue;
11335           int vtkType = grid->GetCellType(vtkId);
11336           int downId = grid->CellIdToDownId(vtkId);
11337           if (downId < 0)
11338           {
11339             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11340             continue; // not OK at this stage of the algorithm:
11341             //no cells created after BuildDownWardConnectivity
11342           }
11343           DownIdType aCell(downId, vtkType);
11344           cellDomains[aCell][idomain] = vtkId;
11345           celldom[vtkId] = idomain;
11346           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11347         }
11348       }
11349     }
11350   }
11351
11352   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11353   //     for each shared face, get the nodes
11354   //     for each node, for each domain of the face, create a clone of the node
11355
11356   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11357   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11358   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11359
11360   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11361   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11362   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11363
11364   //MESSAGE(".. Duplication of the nodes");
11365   for (int idomain = idom0; idomain < nbDomains; idomain++)
11366   {
11367     itface = faceDomains.begin();
11368     for (; itface != faceDomains.end(); ++itface)
11369     {
11370       const std::map<int, int>& domvol = itface->second;
11371       if (!domvol.count(idomain))
11372         continue;
11373       DownIdType face = itface->first;
11374       //MESSAGE(" --- face " << face.cellId);
11375       std::set<int> oldNodes;
11376       oldNodes.clear();
11377       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11378       std::set<int>::iterator itn = oldNodes.begin();
11379       for (; itn != oldNodes.end(); ++itn)
11380       {
11381         int oldId = *itn;
11382         if (nodeDomains[oldId].empty())
11383         {
11384           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11385           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11386         }
11387         std::map<int, int>::const_iterator itdom = domvol.begin();
11388         for (; itdom != domvol.end(); ++itdom)
11389         {
11390           int idom = itdom->first;
11391           //MESSAGE("         domain " << idom);
11392           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11393           {
11394             if (nodeDomains[oldId].size() >= 2) // a multiple node
11395             {
11396               vector<int> orderedDoms;
11397               //MESSAGE("multiple node " << oldId);
11398               if (mutipleNodes.count(oldId))
11399                 orderedDoms = mutipleNodes[oldId];
11400               else
11401               {
11402                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11403                 for (; it != nodeDomains[oldId].end(); ++it)
11404                   orderedDoms.push_back(it->first);
11405               }
11406               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11407               //stringstream txt;
11408               //for (int i=0; i<orderedDoms.size(); i++)
11409               //  txt << orderedDoms[i] << " ";
11410               //MESSAGE("orderedDoms " << txt.str());
11411               mutipleNodes[oldId] = orderedDoms;
11412             }
11413             double *coords = grid->GetPoint(oldId);
11414             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11415             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11416             int newId = newNode->getVtkId();
11417             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11418             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11419           }
11420         }
11421       }
11422     }
11423   }
11424
11425   //MESSAGE(".. Creation of elements");
11426   for (int idomain = idom0; idomain < nbDomains; idomain++)
11427   {
11428     itface = faceDomains.begin();
11429     for (; itface != faceDomains.end(); ++itface)
11430     {
11431       std::map<int, int> domvol = itface->second;
11432       if (!domvol.count(idomain))
11433         continue;
11434       DownIdType face = itface->first;
11435       //MESSAGE(" --- face " << face.cellId);
11436       std::set<int> oldNodes;
11437       oldNodes.clear();
11438       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11439       int nbMultipleNodes = 0;
11440       std::set<int>::iterator itn = oldNodes.begin();
11441       for (; itn != oldNodes.end(); ++itn)
11442       {
11443         int oldId = *itn;
11444         if (mutipleNodes.count(oldId))
11445           nbMultipleNodes++;
11446       }
11447       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11448       {
11449         //MESSAGE("multiple Nodes detected on a shared face");
11450         int downId = itface->first.cellId;
11451         unsigned char cellType = itface->first.cellType;
11452         // --- shared edge or shared face ?
11453         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11454         {
11455           int nodes[3];
11456           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11457           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11458             if (mutipleNodes.count(nodes[i]))
11459               if (!mutipleNodesToFace.count(nodes[i]))
11460                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11461         }
11462         else // shared face (between two volumes)
11463         {
11464           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11465           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11466           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11467           for (int ie =0; ie < nbEdges; ie++)
11468           {
11469             int nodes[3];
11470             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11471             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11472             {
11473               vector<int> vn0 = mutipleNodes[nodes[0]];
11474               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11475               vector<int> doms;
11476               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11477                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11478                   if ( vn0[i0] == vn1[i1] )
11479                     doms.push_back( vn0[ i0 ]);
11480               if ( doms.size() > 2 )
11481               {
11482                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11483                 double *coords = grid->GetPoint(nodes[0]);
11484                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11485                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11486                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11487                 gp_Pnt gref;
11488                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11489                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11490                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11491                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11492                 for ( size_t id = 0; id < doms.size(); id++ )
11493                 {
11494                   int idom = doms[id];
11495                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11496                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11497                   {
11498                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11499                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11500                     if (domain.count(elem))
11501                     {
11502                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11503                       domvol[idom] = svol;
11504                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11505                       double values[3];
11506                       vtkIdType npts = 0;
11507                       vtkIdType* pts = 0;
11508                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11509                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11510                       if (id ==0)
11511                       {
11512                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11513                         angleDom[idom] = 0;
11514                       }
11515                       else
11516                       {
11517                         gp_Pnt g(values[0], values[1], values[2]);
11518                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11519                         //MESSAGE("  angle=" << angleDom[idom]);
11520                       }
11521                       break;
11522                     }
11523                   }
11524                 }
11525                 map<double, int> sortedDom; // sort domains by angle
11526                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11527                   sortedDom[ia->second] = ia->first;
11528                 vector<int> vnodes;
11529                 vector<int> vdom;
11530                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11531                 {
11532                   vdom.push_back(ib->second);
11533                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11534                 }
11535                 for (int ino = 0; ino < nbNodes; ino++)
11536                   vnodes.push_back(nodes[ino]);
11537                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11538               }
11539             }
11540           }
11541         }
11542       }
11543     }
11544   }
11545
11546   // --- iterate on shared faces (volumes to modify, face to extrude)
11547   //     get node id's of the face (id SMDS = id VTK)
11548   //     create flat element with old and new nodes if requested
11549
11550   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11551   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11552
11553   std::map<int, std::map<long,int> > nodeQuadDomains;
11554   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11555
11556   //MESSAGE(".. Creation of elements: simple junction");
11557   if (createJointElems)
11558   {
11559     int idg;
11560     string joints2DName = "joints2D";
11561     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11562     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11563     string joints3DName = "joints3D";
11564     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11565     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11566
11567     itface = faceDomains.begin();
11568     for (; itface != faceDomains.end(); ++itface)
11569     {
11570       DownIdType face = itface->first;
11571       std::set<int> oldNodes;
11572       std::set<int>::iterator itn;
11573       oldNodes.clear();
11574       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11575
11576       std::map<int, int> domvol = itface->second;
11577       std::map<int, int>::iterator itdom = domvol.begin();
11578       int dom1 = itdom->first;
11579       int vtkVolId = itdom->second;
11580       itdom++;
11581       int dom2 = itdom->first;
11582       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11583                                                        nodeQuadDomains);
11584       stringstream grpname;
11585       grpname << "j_";
11586       if (dom1 < dom2)
11587         grpname << dom1 << "_" << dom2;
11588       else
11589         grpname << dom2 << "_" << dom1;
11590       string namegrp = grpname.str();
11591       if (!mapOfJunctionGroups.count(namegrp))
11592         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11593       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11594       if (sgrp)
11595         sgrp->Add(vol->GetID());
11596       if (vol->GetType() == SMDSAbs_Volume)
11597         joints3DGrp->Add(vol->GetID());
11598       else if (vol->GetType() == SMDSAbs_Face)
11599         joints2DGrp->Add(vol->GetID());
11600     }
11601   }
11602
11603   // --- create volumes on multiple domain intersection if requested
11604   //     iterate on mutipleNodesToFace
11605   //     iterate on edgesMultiDomains
11606
11607   //MESSAGE(".. Creation of elements: multiple junction");
11608   if (createJointElems)
11609   {
11610     // --- iterate on mutipleNodesToFace
11611
11612     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11613     for (; itn != mutipleNodesToFace.end(); ++itn)
11614     {
11615       int node = itn->first;
11616       vector<int> orderDom = itn->second;
11617       vector<vtkIdType> orderedNodes;
11618       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11619         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11620       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11621
11622       stringstream grpname;
11623       grpname << "m2j_";
11624       grpname << 0 << "_" << 0;
11625       int idg;
11626       string namegrp = grpname.str();
11627       if (!mapOfJunctionGroups.count(namegrp))
11628         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11629       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11630       if (sgrp)
11631         sgrp->Add(face->GetID());
11632     }
11633
11634     // --- iterate on edgesMultiDomains
11635
11636     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11637     for (; ite != edgesMultiDomains.end(); ++ite)
11638     {
11639       vector<int> nodes = ite->first;
11640       vector<int> orderDom = ite->second;
11641       vector<vtkIdType> orderedNodes;
11642       if (nodes.size() == 2)
11643       {
11644         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11645         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11646           if ( orderDom.size() == 3 )
11647             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11648               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11649           else
11650             for (int idom = orderDom.size()-1; idom >=0; idom--)
11651               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11652         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11653
11654         int idg;
11655         string namegrp = "jointsMultiples";
11656         if (!mapOfJunctionGroups.count(namegrp))
11657           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11658         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11659         if (sgrp)
11660           sgrp->Add(vol->GetID());
11661       }
11662       else
11663       {
11664         //INFOS("Quadratic multiple joints not implemented");
11665         // TODO quadratic nodes
11666       }
11667     }
11668   }
11669
11670   // --- list the explicit faces and edges of the mesh that need to be modified,
11671   //     i.e. faces and edges built with one or more duplicated nodes.
11672   //     associate these faces or edges to their corresponding domain.
11673   //     only the first domain found is kept when a face or edge is shared
11674
11675   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11676   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11677   faceOrEdgeDom.clear();
11678   feDom.clear();
11679
11680   //MESSAGE(".. Modification of elements");
11681   for (int idomain = idom0; idomain < nbDomains; idomain++)
11682   {
11683     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11684     for (; itnod != nodeDomains.end(); ++itnod)
11685     {
11686       int oldId = itnod->first;
11687       //MESSAGE("     node " << oldId);
11688       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11689       for (int i = 0; i < l.ncells; i++)
11690       {
11691         int vtkId = l.cells[i];
11692         int vtkType = grid->GetCellType(vtkId);
11693         int downId = grid->CellIdToDownId(vtkId);
11694         if (downId < 0)
11695           continue; // new cells: not to be modified
11696         DownIdType aCell(downId, vtkType);
11697         int volParents[1000];
11698         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11699         for (int j = 0; j < nbvol; j++)
11700           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11701             if (!feDom.count(vtkId))
11702             {
11703               feDom[vtkId] = idomain;
11704               faceOrEdgeDom[aCell] = emptyMap;
11705               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11706               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11707               //        << " type " << vtkType << " downId " << downId);
11708             }
11709       }
11710     }
11711   }
11712
11713   // --- iterate on shared faces (volumes to modify, face to extrude)
11714   //     get node id's of the face
11715   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11716
11717   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11718   for (int m=0; m<3; m++)
11719   {
11720     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11721     itface = (*amap).begin();
11722     for (; itface != (*amap).end(); ++itface)
11723     {
11724       DownIdType face = itface->first;
11725       std::set<int> oldNodes;
11726       std::set<int>::iterator itn;
11727       oldNodes.clear();
11728       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11729       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11730       std::map<int, int> localClonedNodeIds;
11731
11732       std::map<int, int> domvol = itface->second;
11733       std::map<int, int>::iterator itdom = domvol.begin();
11734       for (; itdom != domvol.end(); ++itdom)
11735       {
11736         int idom = itdom->first;
11737         int vtkVolId = itdom->second;
11738         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11739         localClonedNodeIds.clear();
11740         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11741         {
11742           int oldId = *itn;
11743           if (nodeDomains[oldId].count(idom))
11744           {
11745             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11746             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11747           }
11748         }
11749         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11750       }
11751     }
11752   }
11753
11754   // Remove empty groups (issue 0022812)
11755   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11756   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11757   {
11758     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11759       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11760   }
11761
11762   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11763   grid->DeleteLinks();
11764
11765   CHRONOSTOP(50);
11766   counters::stats();
11767   return true;
11768 }
11769
11770 /*!
11771  * \brief Double nodes on some external faces and create flat elements.
11772  * Flat elements are mainly used by some types of mechanic calculations.
11773  *
11774  * Each group of the list must be constituted of faces.
11775  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11776  * @param theElems - list of groups of faces, where a group of faces is a set of
11777  * SMDS_MeshElements sorted by Id.
11778  * @return TRUE if operation has been completed successfully, FALSE otherwise
11779  */
11780 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11781 {
11782   // MESSAGE("-------------------------------------------------");
11783   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11784   // MESSAGE("-------------------------------------------------");
11785
11786   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11787
11788   // --- For each group of faces
11789   //     duplicate the nodes, create a flat element based on the face
11790   //     replace the nodes of the faces by their clones
11791
11792   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11793   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11794   clonedNodes.clear();
11795   intermediateNodes.clear();
11796   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11797   mapOfJunctionGroups.clear();
11798
11799   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11800   {
11801     const TIDSortedElemSet&           domain = theElems[idom];
11802     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11803     for ( ; elemItr != domain.end(); ++elemItr )
11804     {
11805       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11806       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11807       if (!aFace)
11808         continue;
11809       // MESSAGE("aFace=" << aFace->GetID());
11810       bool isQuad = aFace->IsQuadratic();
11811       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11812
11813       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11814
11815       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11816       while (nodeIt->more())
11817       {
11818         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11819         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11820         if (isMedium)
11821           ln2.push_back(node);
11822         else
11823           ln0.push_back(node);
11824
11825         const SMDS_MeshNode* clone = 0;
11826         if (!clonedNodes.count(node))
11827         {
11828           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11829           copyPosition( node, clone );
11830           clonedNodes[node] = clone;
11831         }
11832         else
11833           clone = clonedNodes[node];
11834
11835         if (isMedium)
11836           ln3.push_back(clone);
11837         else
11838           ln1.push_back(clone);
11839
11840         const SMDS_MeshNode* inter = 0;
11841         if (isQuad && (!isMedium))
11842         {
11843           if (!intermediateNodes.count(node))
11844           {
11845             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11846             copyPosition( node, inter );
11847             intermediateNodes[node] = inter;
11848           }
11849           else
11850             inter = intermediateNodes[node];
11851           ln4.push_back(inter);
11852         }
11853       }
11854
11855       // --- extrude the face
11856
11857       vector<const SMDS_MeshNode*> ln;
11858       SMDS_MeshVolume* vol = 0;
11859       vtkIdType aType = aFace->GetVtkType();
11860       switch (aType)
11861       {
11862       case VTK_TRIANGLE:
11863         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11864         // MESSAGE("vol prism " << vol->GetID());
11865         ln.push_back(ln1[0]);
11866         ln.push_back(ln1[1]);
11867         ln.push_back(ln1[2]);
11868         break;
11869       case VTK_QUAD:
11870         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11871         // MESSAGE("vol hexa " << vol->GetID());
11872         ln.push_back(ln1[0]);
11873         ln.push_back(ln1[1]);
11874         ln.push_back(ln1[2]);
11875         ln.push_back(ln1[3]);
11876         break;
11877       case VTK_QUADRATIC_TRIANGLE:
11878         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11879                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11880         // MESSAGE("vol quad prism " << vol->GetID());
11881         ln.push_back(ln1[0]);
11882         ln.push_back(ln1[1]);
11883         ln.push_back(ln1[2]);
11884         ln.push_back(ln3[0]);
11885         ln.push_back(ln3[1]);
11886         ln.push_back(ln3[2]);
11887         break;
11888       case VTK_QUADRATIC_QUAD:
11889         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11890         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11891         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11892         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11893                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11894                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11895         // MESSAGE("vol quad hexa " << vol->GetID());
11896         ln.push_back(ln1[0]);
11897         ln.push_back(ln1[1]);
11898         ln.push_back(ln1[2]);
11899         ln.push_back(ln1[3]);
11900         ln.push_back(ln3[0]);
11901         ln.push_back(ln3[1]);
11902         ln.push_back(ln3[2]);
11903         ln.push_back(ln3[3]);
11904         break;
11905       case VTK_POLYGON:
11906         break;
11907       default:
11908         break;
11909       }
11910
11911       if (vol)
11912       {
11913         stringstream grpname;
11914         grpname << "jf_";
11915         grpname << idom;
11916         int idg;
11917         string namegrp = grpname.str();
11918         if (!mapOfJunctionGroups.count(namegrp))
11919           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11920         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11921         if (sgrp)
11922           sgrp->Add(vol->GetID());
11923       }
11924
11925       // --- modify the face
11926
11927       aFace->ChangeNodes(&ln[0], ln.size());
11928     }
11929   }
11930   return true;
11931 }
11932
11933 /*!
11934  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11935  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11936  *  groups of faces to remove inside the object, (idem edges).
11937  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11938  */
11939 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11940                                       const TopoDS_Shape&             theShape,
11941                                       SMESH_NodeSearcher*             theNodeSearcher,
11942                                       const char*                     groupName,
11943                                       std::vector<double>&            nodesCoords,
11944                                       std::vector<std::vector<int> >& listOfListOfNodes)
11945 {
11946   // MESSAGE("--------------------------------");
11947   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11948   // MESSAGE("--------------------------------");
11949
11950   // --- zone of volumes to remove is given :
11951   //     1 either by a geom shape (one or more vertices) and a radius,
11952   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11953   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11954   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11955   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11956   //     defined by it's name.
11957
11958   SMESHDS_GroupBase* groupDS = 0;
11959   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11960   while ( groupIt->more() )
11961   {
11962     groupDS = 0;
11963     SMESH_Group * group = groupIt->next();
11964     if ( !group ) continue;
11965     groupDS = group->GetGroupDS();
11966     if ( !groupDS || groupDS->IsEmpty() ) continue;
11967     std::string grpName = group->GetName();
11968     //MESSAGE("grpName=" << grpName);
11969     if (grpName == groupName)
11970       break;
11971     else
11972       groupDS = 0;
11973   }
11974
11975   bool isNodeGroup = false;
11976   bool isNodeCoords = false;
11977   if (groupDS)
11978   {
11979     if (groupDS->GetType() != SMDSAbs_Node)
11980       return;
11981     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11982   }
11983
11984   if (nodesCoords.size() > 0)
11985     isNodeCoords = true; // a list o nodes given by their coordinates
11986   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11987
11988   // --- define groups to build
11989
11990   int idg; // --- group of SMDS volumes
11991   string grpvName = groupName;
11992   grpvName += "_vol";
11993   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11994   if (!grp)
11995   {
11996     MESSAGE("group not created " << grpvName);
11997     return;
11998   }
11999   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12000
12001   int idgs; // --- group of SMDS faces on the skin
12002   string grpsName = groupName;
12003   grpsName += "_skin";
12004   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12005   if (!grps)
12006   {
12007     MESSAGE("group not created " << grpsName);
12008     return;
12009   }
12010   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12011
12012   int idgi; // --- group of SMDS faces internal (several shapes)
12013   string grpiName = groupName;
12014   grpiName += "_internalFaces";
12015   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12016   if (!grpi)
12017   {
12018     MESSAGE("group not created " << grpiName);
12019     return;
12020   }
12021   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12022
12023   int idgei; // --- group of SMDS faces internal (several shapes)
12024   string grpeiName = groupName;
12025   grpeiName += "_internalEdges";
12026   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12027   if (!grpei)
12028   {
12029     MESSAGE("group not created " << grpeiName);
12030     return;
12031   }
12032   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12033
12034   // --- build downward connectivity
12035
12036   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12037   meshDS->BuildDownWardConnectivity(true);
12038   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12039
12040   // --- set of volumes detected inside
12041
12042   std::set<int> setOfInsideVol;
12043   std::set<int> setOfVolToCheck;
12044
12045   std::vector<gp_Pnt> gpnts;
12046   gpnts.clear();
12047
12048   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12049   {
12050     //MESSAGE("group of nodes provided");
12051     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12052     while ( elemIt->more() )
12053     {
12054       const SMDS_MeshElement* elem = elemIt->next();
12055       if (!elem)
12056         continue;
12057       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12058       if (!node)
12059         continue;
12060       SMDS_MeshElement* vol = 0;
12061       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12062       while (volItr->more())
12063       {
12064         vol = (SMDS_MeshElement*)volItr->next();
12065         setOfInsideVol.insert(vol->getVtkId());
12066         sgrp->Add(vol->GetID());
12067       }
12068     }
12069   }
12070   else if (isNodeCoords)
12071   {
12072     //MESSAGE("list of nodes coordinates provided");
12073     size_t i = 0;
12074     int k = 0;
12075     while ( i < nodesCoords.size()-2 )
12076     {
12077       double x = nodesCoords[i++];
12078       double y = nodesCoords[i++];
12079       double z = nodesCoords[i++];
12080       gp_Pnt p = gp_Pnt(x, y ,z);
12081       gpnts.push_back(p);
12082       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12083       k++;
12084     }
12085   }
12086   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12087   {
12088     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12089     TopTools_IndexedMapOfShape vertexMap;
12090     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12091     gp_Pnt p = gp_Pnt(0,0,0);
12092     if (vertexMap.Extent() < 1)
12093       return;
12094
12095     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12096     {
12097       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12098       p = BRep_Tool::Pnt(vertex);
12099       gpnts.push_back(p);
12100       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12101     }
12102   }
12103
12104   if (gpnts.size() > 0)
12105   {
12106     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12107     //MESSAGE("startNode->nodeId " << nodeId);
12108
12109     double radius2 = radius*radius;
12110     //MESSAGE("radius2 " << radius2);
12111
12112     // --- volumes on start node
12113
12114     setOfVolToCheck.clear();
12115     SMDS_MeshElement* startVol = 0;
12116     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12117     while (volItr->more())
12118     {
12119       startVol = (SMDS_MeshElement*)volItr->next();
12120       setOfVolToCheck.insert(startVol->getVtkId());
12121     }
12122     if (setOfVolToCheck.empty())
12123     {
12124       MESSAGE("No volumes found");
12125       return;
12126     }
12127
12128     // --- starting with central volumes then their neighbors, check if they are inside
12129     //     or outside the domain, until no more new neighbor volume is inside.
12130     //     Fill the group of inside volumes
12131
12132     std::map<int, double> mapOfNodeDistance2;
12133     mapOfNodeDistance2.clear();
12134     std::set<int> setOfOutsideVol;
12135     while (!setOfVolToCheck.empty())
12136     {
12137       std::set<int>::iterator it = setOfVolToCheck.begin();
12138       int vtkId = *it;
12139       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12140       bool volInside = false;
12141       vtkIdType npts = 0;
12142       vtkIdType* pts = 0;
12143       grid->GetCellPoints(vtkId, npts, pts);
12144       for (int i=0; i<npts; i++)
12145       {
12146         double distance2 = 0;
12147         if (mapOfNodeDistance2.count(pts[i]))
12148         {
12149           distance2 = mapOfNodeDistance2[pts[i]];
12150           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12151         }
12152         else
12153         {
12154           double *coords = grid->GetPoint(pts[i]);
12155           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12156           distance2 = 1.E40;
12157           for ( size_t j = 0; j < gpnts.size(); j++ )
12158           {
12159             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12160             if (d2 < distance2)
12161             {
12162               distance2 = d2;
12163               if (distance2 < radius2)
12164                 break;
12165             }
12166           }
12167           mapOfNodeDistance2[pts[i]] = distance2;
12168           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12169         }
12170         if (distance2 < radius2)
12171         {
12172           volInside = true; // one or more nodes inside the domain
12173           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12174           break;
12175         }
12176       }
12177       if (volInside)
12178       {
12179         setOfInsideVol.insert(vtkId);
12180         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12181         int neighborsVtkIds[NBMAXNEIGHBORS];
12182         int downIds[NBMAXNEIGHBORS];
12183         unsigned char downTypes[NBMAXNEIGHBORS];
12184         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12185         for (int n = 0; n < nbNeighbors; n++)
12186           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12187             setOfVolToCheck.insert(neighborsVtkIds[n]);
12188       }
12189       else
12190       {
12191         setOfOutsideVol.insert(vtkId);
12192         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12193       }
12194       setOfVolToCheck.erase(vtkId);
12195     }
12196   }
12197
12198   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12199   //     If yes, add the volume to the inside set
12200
12201   bool addedInside = true;
12202   std::set<int> setOfVolToReCheck;
12203   while (addedInside)
12204   {
12205     //MESSAGE(" --------------------------- re check");
12206     addedInside = false;
12207     std::set<int>::iterator itv = setOfInsideVol.begin();
12208     for (; itv != setOfInsideVol.end(); ++itv)
12209     {
12210       int vtkId = *itv;
12211       int neighborsVtkIds[NBMAXNEIGHBORS];
12212       int downIds[NBMAXNEIGHBORS];
12213       unsigned char downTypes[NBMAXNEIGHBORS];
12214       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12215       for (int n = 0; n < nbNeighbors; n++)
12216         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12217           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12218     }
12219     setOfVolToCheck = setOfVolToReCheck;
12220     setOfVolToReCheck.clear();
12221     while  (!setOfVolToCheck.empty())
12222     {
12223       std::set<int>::iterator it = setOfVolToCheck.begin();
12224       int vtkId = *it;
12225       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12226       {
12227         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12228         int countInside = 0;
12229         int neighborsVtkIds[NBMAXNEIGHBORS];
12230         int downIds[NBMAXNEIGHBORS];
12231         unsigned char downTypes[NBMAXNEIGHBORS];
12232         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12233         for (int n = 0; n < nbNeighbors; n++)
12234           if (setOfInsideVol.count(neighborsVtkIds[n]))
12235             countInside++;
12236         //MESSAGE("countInside " << countInside);
12237         if (countInside > 1)
12238         {
12239           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12240           setOfInsideVol.insert(vtkId);
12241           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12242           addedInside = true;
12243         }
12244         else
12245           setOfVolToReCheck.insert(vtkId);
12246       }
12247       setOfVolToCheck.erase(vtkId);
12248     }
12249   }
12250
12251   // --- map of Downward faces at the boundary, inside the global volume
12252   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12253   //     fill group of SMDS faces inside the volume (when several volume shapes)
12254   //     fill group of SMDS faces on the skin of the global volume (if skin)
12255
12256   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12257   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12258   std::set<int>::iterator it = setOfInsideVol.begin();
12259   for (; it != setOfInsideVol.end(); ++it)
12260   {
12261     int vtkId = *it;
12262     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12263     int neighborsVtkIds[NBMAXNEIGHBORS];
12264     int downIds[NBMAXNEIGHBORS];
12265     unsigned char downTypes[NBMAXNEIGHBORS];
12266     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12267     for (int n = 0; n < nbNeighbors; n++)
12268     {
12269       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12270       if (neighborDim == 3)
12271       {
12272         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12273         {
12274           DownIdType face(downIds[n], downTypes[n]);
12275           boundaryFaces[face] = vtkId;
12276         }
12277         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12278         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12279         if (vtkFaceId >= 0)
12280         {
12281           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12282           // find also the smds edges on this face
12283           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12284           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12285           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12286           for (int i = 0; i < nbEdges; i++)
12287           {
12288             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12289             if (vtkEdgeId >= 0)
12290               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12291           }
12292         }
12293       }
12294       else if (neighborDim == 2) // skin of the volume
12295       {
12296         DownIdType face(downIds[n], downTypes[n]);
12297         skinFaces[face] = vtkId;
12298         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12299         if (vtkFaceId >= 0)
12300           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12301       }
12302     }
12303   }
12304
12305   // --- identify the edges constituting the wire of each subshape on the skin
12306   //     define polylines with the nodes of edges, equivalent to wires
12307   //     project polylines on subshapes, and partition, to get geom faces
12308
12309   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12310   std::set<int> emptySet;
12311   emptySet.clear();
12312   std::set<int> shapeIds;
12313
12314   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12315   while (itelem->more())
12316   {
12317     const SMDS_MeshElement *elem = itelem->next();
12318     int shapeId = elem->getshapeId();
12319     int vtkId = elem->getVtkId();
12320     if (!shapeIdToVtkIdSet.count(shapeId))
12321     {
12322       shapeIdToVtkIdSet[shapeId] = emptySet;
12323       shapeIds.insert(shapeId);
12324     }
12325     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12326   }
12327
12328   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12329   std::set<DownIdType, DownIdCompare> emptyEdges;
12330   emptyEdges.clear();
12331
12332   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12333   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12334   {
12335     int shapeId = itShape->first;
12336     //MESSAGE(" --- Shape ID --- "<< shapeId);
12337     shapeIdToEdges[shapeId] = emptyEdges;
12338
12339     std::vector<int> nodesEdges;
12340
12341     std::set<int>::iterator its = itShape->second.begin();
12342     for (; its != itShape->second.end(); ++its)
12343     {
12344       int vtkId = *its;
12345       //MESSAGE("     " << vtkId);
12346       int neighborsVtkIds[NBMAXNEIGHBORS];
12347       int downIds[NBMAXNEIGHBORS];
12348       unsigned char downTypes[NBMAXNEIGHBORS];
12349       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12350       for (int n = 0; n < nbNeighbors; n++)
12351       {
12352         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12353           continue;
12354         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12355         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12356         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12357         {
12358           DownIdType edge(downIds[n], downTypes[n]);
12359           if (!shapeIdToEdges[shapeId].count(edge))
12360           {
12361             shapeIdToEdges[shapeId].insert(edge);
12362             int vtkNodeId[3];
12363             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12364             nodesEdges.push_back(vtkNodeId[0]);
12365             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12366             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12367           }
12368         }
12369       }
12370     }
12371
12372     std::list<int> order;
12373     order.clear();
12374     if (nodesEdges.size() > 0)
12375     {
12376       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12377       nodesEdges[0] = -1;
12378       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12379       nodesEdges[1] = -1; // do not reuse this edge
12380       bool found = true;
12381       while (found)
12382       {
12383         int nodeTofind = order.back(); // try first to push back
12384         int i = 0;
12385         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12386           if (nodesEdges[i] == nodeTofind)
12387             break;
12388         if ( i == (int) nodesEdges.size() )
12389           found = false; // no follower found on back
12390         else
12391         {
12392           if (i%2) // odd ==> use the previous one
12393             if (nodesEdges[i-1] < 0)
12394               found = false;
12395             else
12396             {
12397               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12398               nodesEdges[i-1] = -1;
12399             }
12400           else // even ==> use the next one
12401             if (nodesEdges[i+1] < 0)
12402               found = false;
12403             else
12404             {
12405               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12406               nodesEdges[i+1] = -1;
12407             }
12408         }
12409         if (found)
12410           continue;
12411         // try to push front
12412         found = true;
12413         nodeTofind = order.front(); // try to push front
12414         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12415           if ( nodesEdges[i] == nodeTofind )
12416             break;
12417         if ( i == (int)nodesEdges.size() )
12418         {
12419           found = false; // no predecessor found on front
12420           continue;
12421         }
12422         if (i%2) // odd ==> use the previous one
12423           if (nodesEdges[i-1] < 0)
12424             found = false;
12425           else
12426           {
12427             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12428             nodesEdges[i-1] = -1;
12429           }
12430         else // even ==> use the next one
12431           if (nodesEdges[i+1] < 0)
12432             found = false;
12433           else
12434           {
12435             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12436             nodesEdges[i+1] = -1;
12437           }
12438       }
12439     }
12440
12441
12442     std::vector<int> nodes;
12443     nodes.push_back(shapeId);
12444     std::list<int>::iterator itl = order.begin();
12445     for (; itl != order.end(); itl++)
12446     {
12447       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12448       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12449     }
12450     listOfListOfNodes.push_back(nodes);
12451   }
12452
12453   //     partition geom faces with blocFissure
12454   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12455   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12456
12457   return;
12458 }
12459
12460
12461 //================================================================================
12462 /*!
12463  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12464  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12465  * \return TRUE if operation has been completed successfully, FALSE otherwise
12466  */
12467 //================================================================================
12468
12469 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12470 {
12471   // iterates on volume elements and detect all free faces on them
12472   SMESHDS_Mesh* aMesh = GetMeshDS();
12473   if (!aMesh)
12474     return false;
12475
12476   ElemFeatures faceType( SMDSAbs_Face );
12477   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12478   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12479   while(vIt->more())
12480   {
12481     const SMDS_MeshVolume* volume = vIt->next();
12482     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12483     vTool.SetExternalNormal();
12484     const int iQuad = volume->IsQuadratic();
12485     faceType.SetQuad( iQuad );
12486     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12487     {
12488       if (!vTool.IsFreeFace(iface))
12489         continue;
12490       nbFree++;
12491       vector<const SMDS_MeshNode *> nodes;
12492       int nbFaceNodes = vTool.NbFaceNodes(iface);
12493       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12494       int inode = 0;
12495       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12496         nodes.push_back(faceNodes[inode]);
12497
12498       if (iQuad) // add medium nodes
12499       {
12500         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12501           nodes.push_back(faceNodes[inode]);
12502         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12503           nodes.push_back(faceNodes[8]);
12504       }
12505       // add new face based on volume nodes
12506       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12507       {
12508         nbExisted++; // face already exsist
12509       }
12510       else
12511       {
12512         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12513         nbCreated++;
12514       }
12515     }
12516   }
12517   return ( nbFree == ( nbExisted + nbCreated ));
12518 }
12519
12520 namespace
12521 {
12522   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12523   {
12524     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12525       return n;
12526     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12527   }
12528 }
12529 //================================================================================
12530 /*!
12531  * \brief Creates missing boundary elements
12532  *  \param elements - elements whose boundary is to be checked
12533  *  \param dimension - defines type of boundary elements to create
12534  *  \param group - a group to store created boundary elements in
12535  *  \param targetMesh - a mesh to store created boundary elements in
12536  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12537  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12538  *                                boundary elements will be copied into the targetMesh
12539  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12540  *                                boundary elements will be added into the new group
12541  *  \param aroundElements - if true, elements will be created on boundary of given
12542  *                          elements else, on boundary of the whole mesh.
12543  * \return nb of added boundary elements
12544  */
12545 //================================================================================
12546
12547 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12548                                        Bnd_Dimension           dimension,
12549                                        SMESH_Group*            group/*=0*/,
12550                                        SMESH_Mesh*             targetMesh/*=0*/,
12551                                        bool                    toCopyElements/*=false*/,
12552                                        bool                    toCopyExistingBoundary/*=false*/,
12553                                        bool                    toAddExistingBondary/*= false*/,
12554                                        bool                    aroundElements/*= false*/)
12555 {
12556   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12557   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12558   // hope that all elements are of the same type, do not check them all
12559   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12560     throw SALOME_Exception(LOCALIZED("wrong element type"));
12561
12562   if ( !targetMesh )
12563     toCopyElements = toCopyExistingBoundary = false;
12564
12565   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12566   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12567   int nbAddedBnd = 0;
12568
12569   // editor adding present bnd elements and optionally holding elements to add to the group
12570   SMESH_MeshEditor* presentEditor;
12571   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12572   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12573
12574   SMESH_MesherHelper helper( *myMesh );
12575   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12576   SMDS_VolumeTool vTool;
12577   TIDSortedElemSet avoidSet;
12578   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12579   size_t inode;
12580
12581   typedef vector<const SMDS_MeshNode*> TConnectivity;
12582   TConnectivity tgtNodes;
12583   ElemFeatures elemKind( missType ), elemToCopy;
12584
12585   vector<const SMDS_MeshElement*> presentBndElems;
12586   vector<TConnectivity>           missingBndElems;
12587   vector<int>                     freeFacets;
12588   TConnectivity nodes, elemNodes;
12589
12590   SMDS_ElemIteratorPtr eIt;
12591   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12592   else                  eIt = elemSetIterator( elements );
12593
12594   while (eIt->more())
12595   {
12596     const SMDS_MeshElement* elem = eIt->next();
12597     const int              iQuad = elem->IsQuadratic();
12598     elemKind.SetQuad( iQuad );
12599
12600     // ------------------------------------------------------------------------------------
12601     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12602     // ------------------------------------------------------------------------------------
12603     presentBndElems.clear();
12604     missingBndElems.clear();
12605     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12606     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12607     {
12608       const SMDS_MeshElement* otherVol = 0;
12609       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12610       {
12611         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12612              ( !aroundElements || elements.count( otherVol )))
12613           continue;
12614         freeFacets.push_back( iface );
12615       }
12616       if ( missType == SMDSAbs_Face )
12617         vTool.SetExternalNormal();
12618       for ( size_t i = 0; i < freeFacets.size(); ++i )
12619       {
12620         int                iface = freeFacets[i];
12621         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12622         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12623         if ( missType == SMDSAbs_Edge ) // boundary edges
12624         {
12625           nodes.resize( 2+iQuad );
12626           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12627           {
12628             for ( size_t j = 0; j < nodes.size(); ++j )
12629               nodes[ j ] = nn[ i+j ];
12630             if ( const SMDS_MeshElement* edge =
12631                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12632               presentBndElems.push_back( edge );
12633             else
12634               missingBndElems.push_back( nodes );
12635           }
12636         }
12637         else // boundary face
12638         {
12639           nodes.clear();
12640           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12641             nodes.push_back( nn[inode] ); // add corner nodes
12642           if (iQuad)
12643             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12644               nodes.push_back( nn[inode] ); // add medium nodes
12645           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12646           if ( iCenter > 0 )
12647             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12648
12649           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12650                                                                SMDSAbs_Face, /*noMedium=*/false ))
12651             presentBndElems.push_back( f );
12652           else
12653             missingBndElems.push_back( nodes );
12654
12655           if ( targetMesh != myMesh )
12656           {
12657             // add 1D elements on face boundary to be added to a new mesh
12658             const SMDS_MeshElement* edge;
12659             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12660             {
12661               if ( iQuad )
12662                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12663               else
12664                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12665               if ( edge && avoidSet.insert( edge ).second )
12666                 presentBndElems.push_back( edge );
12667             }
12668           }
12669         }
12670       }
12671     }
12672     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12673     {
12674       avoidSet.clear(), avoidSet.insert( elem );
12675       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12676                         SMDS_MeshElement::iterator() );
12677       elemNodes.push_back( elemNodes[0] );
12678       nodes.resize( 2 + iQuad );
12679       const int nbLinks = elem->NbCornerNodes();
12680       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12681       {
12682         nodes[0] = elemNodes[iN];
12683         nodes[1] = elemNodes[iN+1+iQuad];
12684         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12685           continue; // not free link
12686
12687         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12688         if ( const SMDS_MeshElement* edge =
12689              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12690           presentBndElems.push_back( edge );
12691         else
12692           missingBndElems.push_back( nodes );
12693       }
12694     }
12695
12696     // ---------------------------------
12697     // 2. Add missing boundary elements
12698     // ---------------------------------
12699     if ( targetMesh != myMesh )
12700       // instead of making a map of nodes in this mesh and targetMesh,
12701       // we create nodes with same IDs.
12702       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12703       {
12704         TConnectivity& srcNodes = missingBndElems[i];
12705         tgtNodes.resize( srcNodes.size() );
12706         for ( inode = 0; inode < srcNodes.size(); ++inode )
12707           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12708         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12709                                                                    missType,
12710                                                                    /*noMedium=*/false))
12711           continue;
12712         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12713         ++nbAddedBnd;
12714       }
12715     else
12716       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12717       {
12718         TConnectivity& nodes = missingBndElems[ i ];
12719         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12720                                                                    missType,
12721                                                                    /*noMedium=*/false))
12722           continue;
12723         SMDS_MeshElement* newElem =
12724           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12725         nbAddedBnd += bool( newElem );
12726
12727         // try to set a new element to a shape
12728         if ( myMesh->HasShapeToMesh() )
12729         {
12730           bool ok = true;
12731           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12732           const size_t nbN = nodes.size() / (iQuad+1 );
12733           for ( inode = 0; inode < nbN && ok; ++inode )
12734           {
12735             pair<int, TopAbs_ShapeEnum> i_stype =
12736               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12737             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12738               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12739           }
12740           if ( ok && mediumShapes.size() > 1 )
12741           {
12742             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12743             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12744             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12745             {
12746               if (( ok = ( stype_i->first != stype_i_0.first )))
12747                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12748                                         aMesh->IndexToShape( stype_i_0.second ));
12749             }
12750           }
12751           if ( ok && mediumShapes.begin()->first == missShapeType )
12752             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12753         }
12754       }
12755
12756     // ----------------------------------
12757     // 3. Copy present boundary elements
12758     // ----------------------------------
12759     if ( toCopyExistingBoundary )
12760       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12761       {
12762         const SMDS_MeshElement* e = presentBndElems[i];
12763         tgtNodes.resize( e->NbNodes() );
12764         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12765           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12766         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12767       }
12768     else // store present elements to add them to a group
12769       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12770       {
12771         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12772       }
12773
12774   } // loop on given elements
12775
12776   // ---------------------------------------------
12777   // 4. Fill group with boundary elements
12778   // ---------------------------------------------
12779   if ( group )
12780   {
12781     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12782       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12783         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12784   }
12785   tgtEditor.myLastCreatedElems.Clear();
12786   tgtEditor2.myLastCreatedElems.Clear();
12787
12788   // -----------------------
12789   // 5. Copy given elements
12790   // -----------------------
12791   if ( toCopyElements && targetMesh != myMesh )
12792   {
12793     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12794     else                  eIt = elemSetIterator( elements );
12795     while (eIt->more())
12796     {
12797       const SMDS_MeshElement* elem = eIt->next();
12798       tgtNodes.resize( elem->NbNodes() );
12799       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12800         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12801       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12802
12803       tgtEditor.myLastCreatedElems.Clear();
12804     }
12805   }
12806   return nbAddedBnd;
12807 }
12808
12809 //================================================================================
12810 /*!
12811  * \brief Copy node position and set \a to node on the same geometry
12812  */
12813 //================================================================================
12814
12815 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12816                                      const SMDS_MeshNode* to )
12817 {
12818   if ( !from || !to ) return;
12819
12820   SMDS_PositionPtr pos = from->GetPosition();
12821   if ( !pos || from->getshapeId() < 1 ) return;
12822
12823   switch ( pos->GetTypeOfPosition() )
12824   {
12825   case SMDS_TOP_3DSPACE: break;
12826
12827   case SMDS_TOP_FACE:
12828   {
12829     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12830     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12831                                 fPos->GetUParameter(), fPos->GetVParameter() );
12832     break;
12833   }
12834   case SMDS_TOP_EDGE:
12835   {
12836     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12837     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12838     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12839     break;
12840   }
12841   case SMDS_TOP_VERTEX:
12842   {
12843     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12844     break;
12845   }
12846   case SMDS_TOP_UNSPEC:
12847   default:;
12848   }
12849 }