]> SALOME platform Git repositories - modules/smesh.git/blob - src/SMESH/SMESH_MeshEditor.cxx
Salome HOME
22487: EDF 2778 SMESH: Add a GUI for the flat elements generation
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  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_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
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_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9] );
229       }
230       else if (nbnode == 12) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10], node[11], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12] );
247       }
248       else if (nbnode == 15) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], 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], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523     for ( ; id_sm != id2sm.end(); ++id_sm )
524       if ( id_sm->second->Contains( theElem ))
525         return id_sm->first;
526   }
527
528   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
529   return 0;
530 }
531
532 //=======================================================================
533 //function : IsMedium
534 //purpose  :
535 //=======================================================================
536
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
538                                 const SMDSAbs_ElementType typeToCheck)
539 {
540   bool isMedium = false;
541   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542   while (it->more() && !isMedium ) {
543     const SMDS_MeshElement* elem = it->next();
544     isMedium = elem->IsMediumNode(node);
545   }
546   return isMedium;
547 }
548
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose  : Shift nodes in the array corresponded to quadratic triangle
552 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
554
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 {
557   const SMDS_MeshNode* nd1 = aNodes[0];
558   aNodes[0] = aNodes[1];
559   aNodes[1] = aNodes[2];
560   aNodes[2] = nd1;
561   const SMDS_MeshNode* nd2 = aNodes[3];
562   aNodes[3] = aNodes[4];
563   aNodes[4] = aNodes[5];
564   aNodes[5] = nd2;
565 }
566
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose  : return number of the edges connected with the theNode.
570 //           if theEdges has connections with the other type of the
571 //           elements, return -1
572 //=======================================================================
573
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 {
576   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577   // int nb=0;
578   // while(elemIt->more()) {
579   //   elemIt->next();
580   //   nb++;
581   // }
582   // return nb;
583   return theNode->NbInverseElements();
584 }
585
586 //=======================================================================
587 //function : getNodesFromTwoTria
588 //purpose  : 
589 //=======================================================================
590
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592                                 const SMDS_MeshElement * theTria2,
593                                 vector< const SMDS_MeshNode*>& N1,
594                                 vector< const SMDS_MeshNode*>& N2)
595 {
596   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597   if ( N1.size() < 6 ) return false;
598   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599   if ( N2.size() < 6 ) return false;
600
601   int sames[3] = {-1,-1,-1};
602   int nbsames = 0;
603   int i, j;
604   for(i=0; i<3; i++) {
605     for(j=0; j<3; j++) {
606       if(N1[i]==N2[j]) {
607         sames[i] = j;
608         nbsames++;
609         break;
610       }
611     }
612   }
613   if(nbsames!=2) return false;
614   if(sames[0]>-1) {
615     shiftNodesQuadTria(N1);
616     if(sames[1]>-1) {
617       shiftNodesQuadTria(N1);
618     }
619   }
620   i = sames[0] + sames[1] + sames[2];
621   for(; i<2; i++) {
622     shiftNodesQuadTria(N2);
623   }
624   // now we receive following N1 and N2 (using numeration as in the image below)
625   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
626   // i.e. first nodes from both arrays form a new diagonal
627   return true;
628 }
629
630 //=======================================================================
631 //function : InverseDiag
632 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
633 //           but having other common link.
634 //           Return False if args are improper
635 //=======================================================================
636
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638                                     const SMDS_MeshElement * theTria2 )
639 {
640   MESSAGE("InverseDiag");
641   myLastCreatedElems.Clear();
642   myLastCreatedNodes.Clear();
643
644   if (!theTria1 || !theTria2)
645     return false;
646
647   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648   if (!F1) return false;
649   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650   if (!F2) return false;
651   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653
654     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
656     //    |/ |                                         | \|
657     //  B +--+ 2                                     B +--+ 2
658
659     // put nodes in array and find out indices of the same ones
660     const SMDS_MeshNode* aNodes [6];
661     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662     int i = 0;
663     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664     while ( it->more() ) {
665       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666
667       if ( i > 2 ) // theTria2
668         // find same node of theTria1
669         for ( int j = 0; j < 3; j++ )
670           if ( aNodes[ i ] == aNodes[ j ]) {
671             sameInd[ j ] = i;
672             sameInd[ i ] = j;
673             break;
674           }
675       // next
676       i++;
677       if ( i == 3 ) {
678         if ( it->more() )
679           return false; // theTria1 is not a triangle
680         it = theTria2->nodesIterator();
681       }
682       if ( i == 6 && it->more() )
683         return false; // theTria2 is not a triangle
684     }
685
686     // find indices of 1,2 and of A,B in theTria1
687     int iA = -1, iB = 0, i1 = 0, i2 = 0;
688     for ( i = 0; i < 6; i++ ) {
689       if ( sameInd [ i ] == -1 ) {
690         if ( i < 3 ) i1 = i;
691         else         i2 = i;
692       }
693       else if (i < 3) {
694         if ( iA >= 0) iB = i;
695         else          iA = i;
696       }
697     }
698     // nodes 1 and 2 should not be the same
699     if ( aNodes[ i1 ] == aNodes[ i2 ] )
700       return false;
701
702     // theTria1: A->2
703     aNodes[ iA ] = aNodes[ i2 ];
704     // theTria2: B->1
705     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706
707     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
709
710     return true;
711
712   } // end if(F1 && F2)
713
714   // check case of quadratic faces
715   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717     return false;
718   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
720     return false;
721
722   //       5
723   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
725   //    |   / |
726   //  7 +  +  + 6
727   //    | /9  |
728   //    |/    |
729   //  4 +--+--+ 3
730   //       8
731
732   vector< const SMDS_MeshNode* > N1;
733   vector< const SMDS_MeshNode* > N2;
734   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735     return false;
736   // now we receive following N1 and N2 (using numeration as above image)
737   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
738   // i.e. first nodes from both arrays determ new diagonal
739
740   vector< const SMDS_MeshNode*> N1new( N1.size() );
741   vector< const SMDS_MeshNode*> N2new( N2.size() );
742   N1new.back() = N1.back(); // central node of biquadratic
743   N2new.back() = N2.back();
744   N1new[0] = N1[0];  N2new[0] = N1[0];
745   N1new[1] = N2[0];  N2new[1] = N1[1];
746   N1new[2] = N2[1];  N2new[2] = N2[0];
747   N1new[3] = N1[4];  N2new[3] = N1[3];
748   N1new[4] = N2[3];  N2new[4] = N2[5];
749   N1new[5] = N1[5];  N2new[5] = N1[4];
750   // change nodes in faces
751   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753
754   // move the central node of biquadratic triangle
755   SMESH_MesherHelper helper( *GetMesh() );
756   for ( int is2nd = 0; is2nd < 2; ++is2nd )
757   {
758     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
759     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760     if ( nodes.size() < 7 )
761       continue;
762     helper.SetSubShape( tria->getshapeId() );
763     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
764     gp_Pnt xyz;
765     if ( F.IsNull() )
766     {
767       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768               SMESH_TNodeXYZ( nodes[4] ) +
769               SMESH_TNodeXYZ( nodes[5] )) / 3.;
770     }
771     else
772     {
773       bool checkUV;
774       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777       TopLoc_Location loc;
778       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779       xyz = S->Value( uv.X(), uv.Y() );
780       xyz.Transform( loc );
781       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
782            nodes[6]->getshapeId() > 0 )
783         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784     }
785     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
786   }
787   return true;
788 }
789
790 //=======================================================================
791 //function : findTriangles
792 //purpose  : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
794
795 static bool findTriangles(const SMDS_MeshNode *    theNode1,
796                           const SMDS_MeshNode *    theNode2,
797                           const SMDS_MeshElement*& theTria1,
798                           const SMDS_MeshElement*& theTria2)
799 {
800   if ( !theNode1 || !theNode2 ) return false;
801
802   theTria1 = theTria2 = 0;
803
804   set< const SMDS_MeshElement* > emap;
805   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806   while (it->more()) {
807     const SMDS_MeshElement* elem = it->next();
808     if ( elem->NbCornerNodes() == 3 )
809       emap.insert( elem );
810   }
811   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812   while (it->more()) {
813     const SMDS_MeshElement* elem = it->next();
814     if ( emap.count( elem )) {
815       if ( !theTria1 )
816       {
817         theTria1 = elem;
818       }
819       else  
820       {
821         theTria2 = elem;
822         // theTria1 must be element with minimum ID
823         if ( theTria2->GetID() < theTria1->GetID() )
824           std::swap( theTria2, theTria1 );
825         return true;
826       }
827     }
828   }
829   return false;
830 }
831
832 //=======================================================================
833 //function : InverseDiag
834 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
835 //           with ones built on the same 4 nodes but having other common link.
836 //           Return false if proper faces not found
837 //=======================================================================
838
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840                                     const SMDS_MeshNode * theNode2)
841 {
842   myLastCreatedElems.Clear();
843   myLastCreatedNodes.Clear();
844
845   MESSAGE( "::InverseDiag()" );
846
847   const SMDS_MeshElement *tr1, *tr2;
848   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849     return false;
850
851   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852   if (!F1) return false;
853   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854   if (!F2) return false;
855   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857
858     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
860     //    |/ |                                    | \|
861     //  B +--+ 2                                B +--+ 2
862
863     // put nodes in array
864     // and find indices of 1,2 and of A in tr1 and of B in tr2
865     int i, iA1 = 0, i1 = 0;
866     const SMDS_MeshNode* aNodes1 [3];
867     SMDS_ElemIteratorPtr it;
868     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870       if ( aNodes1[ i ] == theNode1 )
871         iA1 = i; // node A in tr1
872       else if ( aNodes1[ i ] != theNode2 )
873         i1 = i;  // node 1
874     }
875     int iB2 = 0, i2 = 0;
876     const SMDS_MeshNode* aNodes2 [3];
877     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879       if ( aNodes2[ i ] == theNode2 )
880         iB2 = i; // node B in tr2
881       else if ( aNodes2[ i ] != theNode1 )
882         i2 = i;  // node 2
883     }
884
885     // nodes 1 and 2 should not be the same
886     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
887       return false;
888
889     // tr1: A->2
890     aNodes1[ iA1 ] = aNodes2[ i2 ];
891     // tr2: B->1
892     aNodes2[ iB2 ] = aNodes1[ i1 ];
893
894     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
896
897     return true;
898   }
899
900   // check case of quadratic faces
901   return InverseDiag(tr1,tr2);
902 }
903
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
907 //           fusion of triangles tr1 and tr2 having shared link on
908 //           theNode1 and theNode2
909 //=======================================================================
910
911 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
912                         const SMDS_MeshNode *    theNode1,
913                         const SMDS_MeshNode *    theNode2,
914                         const SMDS_MeshElement * tr1,
915                         const SMDS_MeshElement * tr2 )
916 {
917   if( tr1->NbNodes() != tr2->NbNodes() )
918     return false;
919   // find the 4-th node to insert into tr1
920   const SMDS_MeshNode* n4 = 0;
921   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922   int i=0;
923   while ( !n4 && i<3 ) {
924     const SMDS_MeshNode * n = cast2Node( it->next() );
925     i++;
926     bool isDiag = ( n == theNode1 || n == theNode2 );
927     if ( !isDiag )
928       n4 = n;
929   }
930   // Make an array of nodes to be in a quadrangle
931   int iNode = 0, iFirstDiag = -1;
932   it = tr1->nodesIterator();
933   i=0;
934   while ( i<3 ) {
935     const SMDS_MeshNode * n = cast2Node( it->next() );
936     i++;
937     bool isDiag = ( n == theNode1 || n == theNode2 );
938     if ( isDiag ) {
939       if ( iFirstDiag < 0 )
940         iFirstDiag = iNode;
941       else if ( iNode - iFirstDiag == 1 )
942         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943     }
944     else if ( n == n4 ) {
945       return false; // tr1 and tr2 should not have all the same nodes
946     }
947     theQuadNodes[ iNode++ ] = n;
948   }
949   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950     theQuadNodes[ iNode ] = n4;
951
952   return true;
953 }
954
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
958 //           with a quadrangle built on the same 4 nodes.
959 //           Return false if proper faces not found
960 //=======================================================================
961
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963                                    const SMDS_MeshNode * theNode2)
964 {
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   MESSAGE( "::DeleteDiag()" );
969
970   const SMDS_MeshElement *tr1, *tr2;
971   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972     return false;
973
974   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975   if (!F1) return false;
976   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977   if (!F2) return false;
978   SMESHDS_Mesh * aMesh = GetMeshDS();
979
980   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982
983     const SMDS_MeshNode* aNodes [ 4 ];
984     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985       return false;
986
987     const SMDS_MeshElement* newElem = 0;
988     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989     myLastCreatedElems.Append(newElem);
990     AddToSameGroups( newElem, tr1, aMesh );
991     int aShapeId = tr1->getshapeId();
992     if ( aShapeId )
993       {
994         aMesh->SetMeshElementOnShape( newElem, aShapeId );
995       }
996     aMesh->RemoveElement( tr1 );
997     aMesh->RemoveElement( tr2 );
998
999     return true;
1000   }
1001
1002   // check case of quadratic faces
1003   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1006     return false;
1007
1008   //       5
1009   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1011   //    |   / |
1012   //  7 +  +  + 6
1013   //    | /9  |
1014   //    |/    |
1015   //  4 +--+--+ 3
1016   //       8
1017
1018   vector< const SMDS_MeshNode* > N1;
1019   vector< const SMDS_MeshNode* > N2;
1020   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021     return false;
1022   // now we receive following N1 and N2 (using numeration as above image)
1023   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1024   // i.e. first nodes from both arrays determ new diagonal
1025
1026   const SMDS_MeshNode* aNodes[8];
1027   aNodes[0] = N1[0];
1028   aNodes[1] = N1[1];
1029   aNodes[2] = N2[0];
1030   aNodes[3] = N2[1];
1031   aNodes[4] = N1[3];
1032   aNodes[5] = N2[5];
1033   aNodes[6] = N2[3];
1034   aNodes[7] = N1[5];
1035
1036   const SMDS_MeshElement* newElem = 0;
1037   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039   myLastCreatedElems.Append(newElem);
1040   AddToSameGroups( newElem, tr1, aMesh );
1041   int aShapeId = tr1->getshapeId();
1042   if ( aShapeId )
1043     {
1044       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045     }
1046   aMesh->RemoveElement( tr1 );
1047   aMesh->RemoveElement( tr2 );
1048
1049   // remove middle node (9)
1050   GetMeshDS()->RemoveNode( N1[4] );
1051
1052   return true;
1053 }
1054
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose  : Reverse theElement orientation
1058 //=======================================================================
1059
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 {
1062   MESSAGE("Reorient");
1063   myLastCreatedElems.Clear();
1064   myLastCreatedNodes.Clear();
1065
1066   if (!theElem)
1067     return false;
1068   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069   if ( !it || !it->more() )
1070     return false;
1071
1072   const SMDSAbs_ElementType type = theElem->GetType();
1073   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074     return false;
1075
1076   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078   {
1079     const SMDS_VtkVolume* aPolyedre =
1080       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081     if (!aPolyedre) {
1082       MESSAGE("Warning: bad volumic element");
1083       return false;
1084     }
1085     const int nbFaces = aPolyedre->NbFaces();
1086     vector<const SMDS_MeshNode *> poly_nodes;
1087     vector<int> quantities (nbFaces);
1088
1089     // reverse each face of the polyedre
1090     for (int iface = 1; iface <= nbFaces; iface++) {
1091       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092       quantities[iface - 1] = nbFaceNodes;
1093
1094       for (inode = nbFaceNodes; inode >= 1; inode--) {
1095         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096         poly_nodes.push_back(curNode);
1097       }
1098     }
1099     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100   }
1101   else // other elements
1102   {
1103     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105     if ( interlace.empty() )
1106     {
1107       std::reverse( nodes.begin(), nodes.end() ); // polygon
1108     }
1109     else if ( interlace.size() > 1 )
1110     {
1111       SMDS_MeshCell::applyInterlace( interlace, nodes );
1112     }
1113     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1114   }
1115   return false;
1116 }
1117
1118 //================================================================================
1119 /*!
1120  * \brief Reorient faces.
1121  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122  * \param theDirection - desired direction of normal of \a theFace
1123  * \param theFace - one of \a theFaces that sould be oriented according to
1124  *        \a theDirection and whose orientation defines orientation of other faces
1125  * \return number of reoriented faces.
1126  */
1127 //================================================================================
1128
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1130                                   const gp_Dir&            theDirection,
1131                                   const SMDS_MeshElement * theFace)
1132 {
1133   int nbReori = 0;
1134   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135
1136   if ( theFaces.empty() )
1137   {
1138     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139     while ( fIt->more() )
1140       theFaces.insert( theFaces.end(), fIt->next() );
1141   }
1142
1143   // orient theFace according to theDirection
1144   gp_XYZ normal;
1145   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146   if ( normal * theDirection.XYZ() < 0 )
1147     nbReori += Reorient( theFace );
1148
1149   // Orient other faces
1150
1151   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152   TIDSortedElemSet avoidSet;
1153   set< SMESH_TLink > checkedLinks;
1154   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155
1156   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157     theFaces.erase( theFace );
1158   startFaces.insert( theFace );
1159
1160   int nodeInd1, nodeInd2;
1161   const SMDS_MeshElement*           otherFace;
1162   vector< const SMDS_MeshElement* > facesNearLink;
1163   vector< std::pair< int, int > >   nodeIndsOfFace;
1164
1165   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166   while ( !startFaces.empty() )
1167   {
1168     startFace = startFaces.begin();
1169     theFace = *startFace;
1170     startFaces.erase( startFace );
1171     if ( !visitedFaces.insert( theFace ).second )
1172       continue;
1173
1174     avoidSet.clear();
1175     avoidSet.insert(theFace);
1176
1177     NLink link( theFace->GetNode( 0 ), 0 );
1178
1179     const int nbNodes = theFace->NbCornerNodes();
1180     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181     {
1182       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183       linkIt_isNew = checkedLinks.insert( link );
1184       if ( !linkIt_isNew.second )
1185       {
1186         // link has already been checked and won't be encountered more
1187         // if the group (theFaces) is manifold
1188         //checkedLinks.erase( linkIt_isNew.first );
1189       }
1190       else
1191       {
1192         facesNearLink.clear();
1193         nodeIndsOfFace.clear();
1194         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195                                                              theFaces, avoidSet,
1196                                                              &nodeInd1, &nodeInd2 )))
1197           if ( otherFace != theFace)
1198           {
1199             facesNearLink.push_back( otherFace );
1200             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201             avoidSet.insert( otherFace );
1202           }
1203         if ( facesNearLink.size() > 1 )
1204         {
1205           // NON-MANIFOLD mesh shell !
1206           // select a face most co-directed with theFace,
1207           // other faces won't be visited this time
1208           gp_XYZ NF, NOF;
1209           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210           double proj, maxProj = -1;
1211           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213             if (( proj = Abs( NF * NOF )) > maxProj ) {
1214               maxProj = proj;
1215               otherFace = facesNearLink[i];
1216               nodeInd1  = nodeIndsOfFace[i].first;
1217               nodeInd2  = nodeIndsOfFace[i].second;
1218             }
1219           }
1220           // not to visit rejected faces
1221           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223               visitedFaces.insert( facesNearLink[i] );
1224         }
1225         else if ( facesNearLink.size() == 1 )
1226         {
1227           otherFace = facesNearLink[0];
1228           nodeInd1  = nodeIndsOfFace.back().first;
1229           nodeInd2  = nodeIndsOfFace.back().second;
1230         }
1231         if ( otherFace && otherFace != theFace)
1232         {
1233           // link must be reverse in otherFace if orientation ot otherFace
1234           // is same as that of theFace
1235           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236           {
1237             nbReori += Reorient( otherFace );
1238           }
1239           startFaces.insert( otherFace );
1240         }
1241       }
1242       std::swap( link.first, link.second ); // reverse the link
1243     }
1244   }
1245   return nbReori;
1246 }
1247
1248 //=======================================================================
1249 //function : getBadRate
1250 //purpose  :
1251 //=======================================================================
1252
1253 static double getBadRate (const SMDS_MeshElement*               theElem,
1254                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1255 {
1256   SMESH::Controls::TSequenceOfXYZ P;
1257   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1258     return 1e100;
1259   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 }
1262
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose  : Cut quadrangles into triangles.
1266 //           theCrit is used to select a diagonal to cut
1267 //=======================================================================
1268
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1270                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1271 {
1272   myLastCreatedElems.Clear();
1273   myLastCreatedNodes.Clear();
1274
1275   if ( !theCrit.get() )
1276     return false;
1277
1278   SMESHDS_Mesh * aMesh = GetMeshDS();
1279
1280   Handle(Geom_Surface) surface;
1281   SMESH_MesherHelper   helper( *GetMesh() );
1282
1283   TIDSortedElemSet::iterator itElem;
1284   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1285   {
1286     const SMDS_MeshElement* elem = *itElem;
1287     if ( !elem || elem->GetType() != SMDSAbs_Face )
1288       continue;
1289     if ( elem->NbCornerNodes() != 4 )
1290       continue;
1291
1292     // retrieve element nodes
1293     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1294
1295     // compare two sets of possible triangles
1296     double aBadRate1, aBadRate2; // to what extent a set is bad
1297     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1300
1301     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1304
1305     const int aShapeId = FindShape( elem );
1306     const SMDS_MeshElement* newElem1 = 0;
1307     const SMDS_MeshElement* newElem2 = 0;
1308
1309     if ( !elem->IsQuadratic() ) // split liner quadrangle
1310     {
1311       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313       if ( aBadRate1 <= aBadRate2 ) {
1314         // tr1 + tr2 is better
1315         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317       }
1318       else {
1319         // tr3 + tr4 is better
1320         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322       }
1323     }
1324     else // split quadratic quadrangle
1325     {
1326       helper.SetIsQuadratic( true );
1327       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1328
1329       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330       if ( aNodes.size() == 9 )
1331       {
1332         helper.SetIsBiQuadratic( true );
1333         if ( aBadRate1 <= aBadRate2 )
1334           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1335         else
1336           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1337       }
1338       // create a new element
1339       if ( aBadRate1 <= aBadRate2 ) {
1340         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342       }
1343       else {
1344         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1360
1361     aMesh->RemoveElement( elem );
1362   }
1363   return true;
1364 }
1365
1366 //=======================================================================
1367 /*!
1368  * \brief Split each of given quadrangles into 4 triangles.
1369  * \param theElems - The faces to be splitted. If empty all faces are split.
1370  */
1371 //=======================================================================
1372
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   SMESH_MesherHelper helper( *GetMesh() );
1379   helper.SetElementsOnShape( true );
1380
1381   SMDS_ElemIteratorPtr faceIt;
1382   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383   else                    faceIt = elemSetIterator( theElems );
1384
1385   bool   checkUV;
1386   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1387   gp_XYZ xyz[9];
1388   vector< const SMDS_MeshNode* > nodes;
1389   SMESHDS_SubMesh*               subMeshDS;
1390   TopoDS_Face                    F;
1391   Handle(Geom_Surface)           surface;
1392   TopLoc_Location                loc;
1393
1394   while ( faceIt->more() )
1395   {
1396     const SMDS_MeshElement* quad = faceIt->next();
1397     if ( !quad || quad->NbCornerNodes() != 4 )
1398       continue;
1399
1400     // get a surface the quad is on
1401
1402     if ( quad->getshapeId() < 1 )
1403     {
1404       F.Nullify();
1405       helper.SetSubShape( 0 );
1406       subMeshDS = 0;
1407     }
1408     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1409     {
1410       helper.SetSubShape( quad->getshapeId() );
1411       if ( !helper.GetSubShape().IsNull() &&
1412            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1413       {
1414         F = TopoDS::Face( helper.GetSubShape() );
1415         surface = BRep_Tool::Surface( F, loc );
1416         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1417       }
1418       else
1419       {
1420         helper.SetSubShape( 0 );
1421         subMeshDS = 0;
1422       }
1423     }
1424
1425     // create a central node
1426
1427     const SMDS_MeshNode* nCentral;
1428     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1429
1430     if ( nodes.size() == 9 )
1431     {
1432       nCentral = nodes.back();
1433     }
1434     else
1435     {
1436       size_t iN = 0;
1437       if ( F.IsNull() )
1438       {
1439         for ( ; iN < nodes.size(); ++iN )
1440           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1441
1442         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1444
1445         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446                                    xyz[0], xyz[1], xyz[2], xyz[3],
1447                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1448       }
1449       else
1450       {
1451         for ( ; iN < nodes.size(); ++iN )
1452           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1453
1454         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1456
1457         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458                                   uv[0], uv[1], uv[2], uv[3],
1459                                   uv[4], uv[5], uv[6], uv[7] );
1460
1461         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1462         xyz[ 8 ] = p.XYZ();
1463       }
1464
1465       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466                                  uv[8].X(), uv[8].Y() );
1467       myLastCreatedNodes.Append( nCentral );
1468     }
1469
1470     // create 4 triangles
1471
1472     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1473     
1474     helper.SetIsQuadratic  ( nodes.size() > 4 );
1475     helper.SetIsBiQuadratic( nodes.size() == 9 );
1476     if ( helper.GetIsQuadratic() )
1477       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1478
1479     for ( int i = 0; i < 4; ++i )
1480     {
1481       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482                                                nodes[(i+1)%4],
1483                                                nCentral );
1484       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485       myLastCreatedElems.Append( tria );
1486     }
1487   }
1488 }
1489
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose  : Find better diagonal for cutting.
1493 //=======================================================================
1494
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1496                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1497 {
1498   myLastCreatedElems.Clear();
1499   myLastCreatedNodes.Clear();
1500
1501   if (!theCrit.get())
1502     return -1;
1503
1504   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505     return -1;
1506
1507   if( theQuad->NbNodes()==4 ||
1508       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1509
1510     // retrieve element nodes
1511     const SMDS_MeshNode* aNodes [4];
1512     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1513     int i = 0;
1514     //while (itN->more())
1515     while (i<4) {
1516       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1517     }
1518     // compare two sets of possible triangles
1519     double aBadRate1, aBadRate2; // to what extent a set is bad
1520     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1523
1524     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530       return 1; // diagonal 1-3
1531
1532     return 2; // diagonal 2-4
1533   }
1534   return -1;
1535 }
1536
1537 namespace
1538 {
1539   // Methods of splitting volumes into tetra
1540
1541   const int theHexTo5_1[5*4+1] =
1542     {
1543       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1544     };
1545   const int theHexTo5_2[5*4+1] =
1546     {
1547       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1548     };
1549   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1550
1551   const int theHexTo6_1[6*4+1] =
1552     {
1553       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
1554     };
1555   const int theHexTo6_2[6*4+1] =
1556     {
1557       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
1558     };
1559   const int theHexTo6_3[6*4+1] =
1560     {
1561       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
1562     };
1563   const int theHexTo6_4[6*4+1] =
1564     {
1565       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
1566     };
1567   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1568
1569   const int thePyraTo2_1[2*4+1] =
1570     {
1571       0, 1, 2, 4,    0, 2, 3, 4,   -1
1572     };
1573   const int thePyraTo2_2[2*4+1] =
1574     {
1575       1, 2, 3, 4,    1, 3, 0, 4,   -1
1576     };
1577   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1578
1579   const int thePentaTo3_1[3*4+1] =
1580     {
1581       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1582     };
1583   const int thePentaTo3_2[3*4+1] =
1584     {
1585       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1586     };
1587   const int thePentaTo3_3[3*4+1] =
1588     {
1589       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1590     };
1591   const int thePentaTo3_4[3*4+1] =
1592     {
1593       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1594     };
1595   const int thePentaTo3_5[3*4+1] =
1596     {
1597       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1598     };
1599   const int thePentaTo3_6[3*4+1] =
1600     {
1601       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1602     };
1603   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1605
1606   // Methods of splitting hexahedron into prisms
1607
1608   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1609     {
1610       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
1611     };
1612   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1613     {
1614       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
1615     };
1616   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1617     {
1618       0, 3, 8, 1, 2, 9,    3, 7, 8, 2, 6, 9,    7, 4, 8, 6, 5, 9,   4, 0, 8, 5, 1, 9,    -1
1619     };
1620
1621   const int theHexTo2Prisms_BT_1[6*2+1] =
1622     {
1623       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1624     };
1625   const int theHexTo2Prisms_BT_2[6*2+1] =
1626     {
1627       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1628     };
1629   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1630
1631   const int theHexTo2Prisms_LR_1[6*2+1] =
1632     {
1633       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1634     };
1635   const int theHexTo2Prisms_LR_2[6*2+1] =
1636     {
1637       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1638     };
1639   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1640
1641   const int theHexTo2Prisms_FB_1[6*2+1] =
1642     {
1643       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1644     };
1645   const int theHexTo2Prisms_FB_2[6*2+1] =
1646     {
1647       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1648     };
1649   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1650
1651
1652   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1653   {
1654     int _n1, _n2, _n3;
1655     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1658                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1659   };
1660   struct TSplitMethod
1661   {
1662     int        _nbSplits;
1663     int        _nbCorners;
1664     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1666     bool       _ownConn;      //!< to delete _connectivity in destructor
1667     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1668
1669     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672     bool hasFacet( const TTriangleFacet& facet ) const
1673     {
1674       if ( _nbCorners == 4 )
1675       {
1676         const int* tetConn = _connectivity;
1677         for ( ; tetConn[0] >= 0; tetConn += 4 )
1678           if (( facet.contains( tetConn[0] ) +
1679                 facet.contains( tetConn[1] ) +
1680                 facet.contains( tetConn[2] ) +
1681                 facet.contains( tetConn[3] )) == 3 )
1682             return true;
1683       }
1684       else // prism, _nbCorners == 6
1685       {
1686         const int* prismConn = _connectivity;
1687         for ( ; prismConn[0] >= 0; prismConn += 6 )
1688         {
1689           if (( facet.contains( prismConn[0] ) &&
1690                 facet.contains( prismConn[1] ) &&
1691                 facet.contains( prismConn[2] ))
1692               ||
1693               ( facet.contains( prismConn[3] ) &&
1694                 facet.contains( prismConn[4] ) &&
1695                 facet.contains( prismConn[5] )))
1696             return true;
1697         }
1698       }
1699       return false;
1700     }
1701   };
1702
1703   //=======================================================================
1704   /*!
1705    * \brief return TSplitMethod for the given element to split into tetrahedra
1706    */
1707   //=======================================================================
1708
1709   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1710   {
1711     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1712
1713     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714     // an edge and a face barycenter; tertaherdons are based on triangles and
1715     // a volume barycenter
1716     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1717
1718     // Find out how adjacent volumes are split
1719
1720     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1723     {
1724       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726       if ( nbNodes < 4 ) continue;
1727
1728       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729       const int* nInd = vol.GetFaceNodesIndices( iF );
1730       if ( nbNodes == 4 )
1731       {
1732         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1736       }
1737       else
1738       {
1739         int iCom = 0; // common node of triangle faces to split into
1740         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1741         {
1742           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1743                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1744                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1746                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1747                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1749           {
1750             triaSplits.push_back( t012 );
1751             triaSplits.push_back( t023 );
1752             break;
1753           }
1754         }
1755       }
1756       if ( !triaSplits.empty() )
1757         hasAdjacentSplits = true;
1758     }
1759
1760     // Among variants of split method select one compliant with adjacent volumes
1761
1762     TSplitMethod method;
1763     if ( !vol.Element()->IsPoly() && !is24TetMode )
1764     {
1765       int nbVariants = 2, nbTet = 0;
1766       const int** connVariants = 0;
1767       switch ( vol.Element()->GetEntityType() )
1768       {
1769       case SMDSEntity_Hexa:
1770       case SMDSEntity_Quad_Hexa:
1771       case SMDSEntity_TriQuad_Hexa:
1772         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773           connVariants = theHexTo5, nbTet = 5;
1774         else
1775           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1776         break;
1777       case SMDSEntity_Pyramid:
1778       case SMDSEntity_Quad_Pyramid:
1779         connVariants = thePyraTo2;  nbTet = 2;
1780         break;
1781       case SMDSEntity_Penta:
1782       case SMDSEntity_Quad_Penta:
1783         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1784         break;
1785       default:
1786         nbVariants = 0;
1787       }
1788       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1789       {
1790         // check method compliancy with adjacent tetras,
1791         // all found splits must be among facets of tetras described by this method
1792         method = TSplitMethod( nbTet, connVariants[variant] );
1793         if ( hasAdjacentSplits && method._nbSplits > 0 )
1794         {
1795           bool facetCreated = true;
1796           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1797           {
1798             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800               facetCreated = method.hasFacet( *facet );
1801           }
1802           if ( !facetCreated )
1803             method = TSplitMethod(0); // incompatible method
1804         }
1805       }
1806     }
1807     if ( method._nbSplits < 1 )
1808     {
1809       // No standard method is applicable, use a generic solution:
1810       // each facet of a volume is split into triangles and
1811       // each of triangles and a volume barycenter form a tetrahedron.
1812
1813       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1814
1815       int* connectivity = new int[ maxTetConnSize + 1 ];
1816       method._connectivity = connectivity;
1817       method._ownConn = true;
1818       method._baryNode = !isHex27; // to create central node or not
1819
1820       int connSize = 0;
1821       int baryCenInd = vol.NbNodes() - int( isHex27 );
1822       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1823       {
1824         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825         const int*   nInd = vol.GetFaceNodesIndices( iF );
1826         // find common node of triangle facets of tetra to create
1827         int iCommon = 0; // index in linear numeration
1828         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829         if ( !triaSplits.empty() )
1830         {
1831           // by found facets
1832           const TTriangleFacet* facet = &triaSplits.front();
1833           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1836               break;
1837         }
1838         else if ( nbNodes > 3 && !is24TetMode )
1839         {
1840           // find the best method of splitting into triangles by aspect ratio
1841           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842           map< double, int > badness2iCommon;
1843           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1846           {
1847             double badness = 0;
1848             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1849             {
1850               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1851                                       nodes[ iQ*((iLast-1)%nbNodes)],
1852                                       nodes[ iQ*((iLast  )%nbNodes)]);
1853               badness += getBadRate( &tria, aspectRatio );
1854             }
1855             badness2iCommon.insert( make_pair( badness, iCommon ));
1856           }
1857           // use iCommon with lowest badness
1858           iCommon = badness2iCommon.begin()->second;
1859         }
1860         if ( iCommon >= nbNodes )
1861           iCommon = 0; // something wrong
1862
1863         // fill connectivity of tetrahedra based on a current face
1864         int nbTet = nbNodes - 2;
1865         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1866         {
1867           int faceBaryCenInd;
1868           if ( isHex27 )
1869           {
1870             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1872           }
1873           else
1874           {
1875             method._faceBaryNode[ iF ] = 0;
1876             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1877           }
1878           nbTet = nbNodes;
1879           for ( int i = 0; i < nbTet; ++i )
1880           {
1881             int i1 = i, i2 = (i+1) % nbNodes;
1882             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885             connectivity[ connSize++ ] = faceBaryCenInd;
1886             connectivity[ connSize++ ] = baryCenInd;
1887           }
1888         }
1889         else
1890         {
1891           for ( int i = 0; i < nbTet; ++i )
1892           {
1893             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898             connectivity[ connSize++ ] = baryCenInd;
1899           }
1900         }
1901         method._nbSplits += nbTet;
1902
1903       } // loop on volume faces
1904
1905       connectivity[ connSize++ ] = -1;
1906
1907     } // end of generic solution
1908
1909     return method;
1910   }
1911   //=======================================================================
1912   /*!
1913    * \brief return TSplitMethod to split haxhedron into prisms
1914    */
1915   //=======================================================================
1916
1917   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918                                     const int        methodFlags,
1919                                     const int        facetToSplit)
1920   {
1921     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1922     // B, T, L, B, R, F
1923     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1924
1925     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1926     {
1927       static TSplitMethod to4methods[4]; // order BT, LR, FB
1928       if ( to4methods[iF]._nbSplits == 0 )
1929       {
1930         switch ( iF ) {
1931         case 0:
1932           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933           to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934           to4methods[iF]._faceBaryNode[ 1 ] = 0;
1935           break;
1936         case 1:
1937           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938           to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939           to4methods[iF]._faceBaryNode[ 4 ] = 0;
1940           break;
1941         case 2:
1942           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943           to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944           to4methods[iF]._faceBaryNode[ 5 ] = 0;
1945           break;
1946         default: return to4methods[3];
1947         }
1948         to4methods[iF]._nbSplits  = 4;
1949         to4methods[iF]._nbCorners = 6;
1950       }
1951       return to4methods[iF];
1952     }
1953     // else if ( methodFlags == HEXA_TO_2_PRISMS )
1954
1955     TSplitMethod method;
1956
1957     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1958
1959     const int nbVariants = 2, nbSplits = 2;
1960     const int** connVariants = 0;
1961     switch ( iF ) {
1962     case 0: connVariants = theHexTo2Prisms_BT; break;
1963     case 1: connVariants = theHexTo2Prisms_LR; break;
1964     case 2: connVariants = theHexTo2Prisms_FB; break;
1965     default: return method;
1966     }
1967
1968     // look for prisms adjacent via facetToSplit and an opposite one
1969     for ( int is2nd = 0; is2nd < 2; ++is2nd )
1970     {
1971       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973       if ( nbNodes != 4 ) return method;
1974
1975       const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1978       TTriangleFacet* t;
1979       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1980         t = &t012;
1981       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1982         t = &t123;
1983       else
1984         continue;
1985
1986       // there are adjacent prism
1987       for ( int variant = 0; variant < nbVariants; ++variant )
1988       {
1989         // check method compliancy with adjacent prisms,
1990         // the found prism facets must be among facets of prisms described by current method
1991         method._nbSplits     = nbSplits;
1992         method._nbCorners    = 6;
1993         method._connectivity = connVariants[ variant ];
1994         if ( method.hasFacet( *t ))
1995           return method;
1996       }
1997     }
1998
1999     // No adjacent prisms. Select a variant with a best aspect ratio.
2000
2001     double badness[2] = { 0, 0 };
2002     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003     const SMDS_MeshNode** nodes = vol.GetNodes();
2004     for ( int variant = 0; variant < nbVariants; ++variant )
2005       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2006       {
2007         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2009
2010         method._connectivity = connVariants[ variant ];
2011         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2014
2015         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2016                                 nodes[ t->_n2 ],
2017                                 nodes[ t->_n3 ] );
2018         badness[ variant ] += getBadRate( &tria, aspectRatio );
2019       }
2020     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2021
2022     method._nbSplits     = nbSplits;
2023     method._nbCorners    = 6;
2024     method._connectivity = connVariants[ iBetter ];
2025
2026     return method;
2027   }
2028
2029   //================================================================================
2030   /*!
2031    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2032    */
2033   //================================================================================
2034
2035   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2036                                        const SMDSAbs_GeometryType geom ) const
2037   {
2038     // find the tetrahedron including the three nodes of facet
2039     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043     while ( volIt1->more() )
2044     {
2045       const SMDS_MeshElement* v = volIt1->next();
2046       if ( v->GetGeomType() != geom )
2047         continue;
2048       const int lastCornerInd = v->NbCornerNodes() - 1;
2049       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050         continue; // medium node not allowed
2051       const int ind2 = v->GetNodeIndex( n2 );
2052       if ( ind2 < 0 || lastCornerInd < ind2 )
2053         continue;
2054       const int ind3 = v->GetNodeIndex( n3 );
2055       if ( ind3 < 0 || lastCornerInd < ind3 )
2056         continue;
2057       return true;
2058     }
2059     return false;
2060   }
2061
2062   //=======================================================================
2063   /*!
2064    * \brief A key of a face of volume
2065    */
2066   //=======================================================================
2067
2068   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2069   {
2070     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2071     {
2072       TIDSortedNodeSet sortedNodes;
2073       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074       int nbNodes = vol.NbFaceNodes( iF );
2075       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076       for ( int i = 0; i < nbNodes; i += iQ )
2077         sortedNodes.insert( fNodes[i] );
2078       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079       first.first   = (*(n++))->GetID();
2080       first.second  = (*(n++))->GetID();
2081       second.first  = (*(n++))->GetID();
2082       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2083     }
2084   };
2085 } // namespace
2086
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose  : Split volume elements into tetrahedra or prisms.
2090 //           If facet ID < 0, element is split into tetrahedra,
2091 //           else a hexahedron is split into prisms so that the given facet is
2092 //           split into triangles
2093 //=======================================================================
2094
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096                                      const int            theMethodFlags)
2097 {
2098   // std-like iterator on coordinates of nodes of mesh element
2099   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100   NXyzIterator xyzEnd;
2101
2102   SMDS_VolumeTool    volTool;
2103   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104   fHelper.ToFixNodeParameters( true );
2105
2106   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2108
2109   SMESH_SequenceOfElemPtr newNodes, newElems;
2110
2111   // map face of volume to it's baricenrtic node
2112   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2113   double bc[3];
2114
2115   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116   for ( ; elem2facet != theElems.end(); ++elem2facet )
2117   {
2118     const SMDS_MeshElement* elem = elem2facet->first;
2119     const int       facetToSplit = elem2facet->second;
2120     if ( elem->GetType() != SMDSAbs_Volume )
2121       continue;
2122     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2124       continue;
2125
2126     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2127
2128     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2129                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2130                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131     if ( splitMethod._nbSplits < 1 ) continue;
2132
2133     // find submesh to add new tetras to
2134     if ( !subMesh || !subMesh->Contains( elem ))
2135     {
2136       int shapeID = FindShape( elem );
2137       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138       subMesh = GetMeshDS()->MeshElements( shapeID );
2139     }
2140     int iQ;
2141     if ( elem->IsQuadratic() )
2142     {
2143       iQ = 2;
2144       // add quadratic links to the helper
2145       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2146       {
2147         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149         for ( int iN = 0; iN < nbN; iN += iQ )
2150           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2151       }
2152       helper.SetIsQuadratic( true );
2153     }
2154     else
2155     {
2156       iQ = 1;
2157       helper.SetIsQuadratic( false );
2158     }
2159     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160                                         volTool.GetNodes() + elem->NbNodes() );
2161     helper.SetElementsOnShape( true );
2162     if ( splitMethod._baryNode )
2163     {
2164       // make a node at barycenter
2165       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167       nodes.push_back( gcNode );
2168       newNodes.Append( gcNode );
2169     }
2170     if ( !splitMethod._faceBaryNode.empty() )
2171     {
2172       // make or find baricentric nodes of faces
2173       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2175       {
2176         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177           volFace2BaryNode.insert
2178           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2179         if ( !f_n->second )
2180         {
2181           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2183         }
2184         nodes.push_back( iF_n->second = f_n->second );
2185       }
2186     }
2187
2188     // make new volumes
2189     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190     const int* volConn = splitMethod._connectivity;
2191     if ( splitMethod._nbCorners == 4 ) // tetra
2192       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194                                                             nodes[ volConn[1] ],
2195                                                             nodes[ volConn[2] ],
2196                                                             nodes[ volConn[3] ]));
2197     else // prisms
2198       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200                                                             nodes[ volConn[1] ],
2201                                                             nodes[ volConn[2] ],
2202                                                             nodes[ volConn[3] ],
2203                                                             nodes[ volConn[4] ],
2204                                                             nodes[ volConn[5] ]));
2205
2206     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2207
2208     // Split faces on sides of the split volume
2209
2210     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2212     {
2213       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214       if ( nbNodes < 4 ) continue;
2215
2216       // find an existing face
2217       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220                                                                        /*noMedium=*/false))
2221       {
2222         // make triangles
2223         helper.SetElementsOnShape( false );
2224         vector< const SMDS_MeshElement* > triangles;
2225
2226         // find submesh to add new triangles in
2227         if ( !fSubMesh || !fSubMesh->Contains( face ))
2228         {
2229           int shapeID = FindShape( face );
2230           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2231         }
2232         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233         if ( iF_n != splitMethod._faceBaryNode.end() )
2234         {
2235           const SMDS_MeshNode *baryNode = iF_n->second;
2236           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2237           {
2238             const SMDS_MeshNode* n1 = fNodes[iN];
2239             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240             const SMDS_MeshNode *n3 = baryNode;
2241             if ( !volTool.IsFaceExternal( iF ))
2242               swap( n2, n3 );
2243             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2244           }
2245           if ( fSubMesh ) // update position of the bary node on geometry
2246           {
2247             if ( subMesh )
2248               subMesh->RemoveNode( baryNode, false );
2249             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2252             {
2253               fHelper.SetSubShape( s );
2254               gp_XY uv( 1e100, 1e100 );
2255               double distXYZ[4];
2256               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2258                    uv.X() < 1e100 )
2259               {
2260                 // node is too far from the surface
2261                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2264               }
2265             }
2266           }
2267         }
2268         else
2269         {
2270           // among possible triangles create ones discribed by split method
2271           const int* nInd = volTool.GetFaceNodesIndices( iF );
2272           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273           int iCom = 0; // common node of triangle faces to split into
2274           list< TTriangleFacet > facets;
2275           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2276           {
2277             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2278                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2279                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2281                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2282                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2284             {
2285               facets.push_back( t012 );
2286               facets.push_back( t023 );
2287               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2289                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2290                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2291               break;
2292             }
2293           }
2294           list< TTriangleFacet >::iterator facet = facets.begin();
2295           if ( facet == facets.end() )
2296             break;
2297           for ( ; facet != facets.end(); ++facet )
2298           {
2299             if ( !volTool.IsFaceExternal( iF ))
2300               swap( facet->_n2, facet->_n3 );
2301             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302                                                  volNodes[ facet->_n2 ],
2303                                                  volNodes[ facet->_n3 ]));
2304           }
2305         }
2306         for ( int i = 0; i < triangles.size(); ++i )
2307         {
2308           if ( !triangles[i] ) continue;
2309           if ( fSubMesh )
2310             fSubMesh->AddElement( triangles[i]);
2311           newElems.Append( triangles[i] );
2312         }
2313         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2315
2316       } // while a face based on facet nodes exists
2317     } // loop on volume faces to split them into triangles
2318
2319     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2320
2321     if ( geomType == SMDSEntity_TriQuad_Hexa )
2322     {
2323       // remove medium nodes that could become free
2324       for ( int i = 20; i < volTool.NbNodes(); ++i )
2325         if ( volNodes[i]->NbInverseElements() == 0 )
2326           GetMeshDS()->RemoveNode( volNodes[i] );
2327     }
2328   } // loop on volumes to split
2329   
2330   myLastCreatedNodes = newNodes;
2331   myLastCreatedElems = newElems;
2332 }
2333
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose  : For hexahedra that will be split into prisms, finds facets to
2337 //           split into triangles. Only hexahedra adjacent to the one closest
2338 //           to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in]     theFacetNormal - facet normal
2341 //param [out]    theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2343
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345                                              const gp_Ax1&     theFacetNormal,
2346                                              TFacetOfElem &    theFacets)
2347 {
2348   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2349
2350   // Find a hexa closest to the location of theFacetNormal
2351
2352   const SMDS_MeshElement* startHex;
2353   {
2354     // get SMDS_ElemIteratorPtr on theHexas
2355     typedef const SMDS_MeshElement*                                      TValue;
2356     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2357     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2358     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2359     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361       ( new TElemSetIter( theHexas.begin(),
2362                           theHexas.end(),
2363                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2364
2365     SMESH_ElementSearcher* searcher =
2366       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2367
2368     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2369
2370     delete searcher;
2371
2372     if ( !startHex )
2373       throw SALOME_Exception( THIS_METHOD "startHex not found");
2374   }
2375
2376   // Select a facet of startHex by theFacetNormal
2377
2378   SMDS_VolumeTool vTool( startHex );
2379   double norm[3], dot, maxDot = 0;
2380   int facetID = -1;
2381   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2383     {
2384       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2385       if ( dot > maxDot )
2386       {
2387         facetID = iF;
2388         maxDot = dot;
2389       }
2390     }
2391   if ( facetID < 0 )
2392     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2393
2394   // Fill theFacets starting from facetID of startHex
2395
2396   // facets used for seach of volumes adjacent to already treated ones
2397   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2399   TFacetMap facetsToCheck;
2400
2401   set<const SMDS_MeshNode*> facetNodes;
2402   const SMDS_MeshElement*   curHex;
2403
2404   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2405
2406   while ( startHex )
2407   {
2408     // move in two directions from startHex via facetID
2409     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2410     {
2411       curHex       = startHex;
2412       int curFacet = facetID;
2413       if ( is2nd ) // do not treat startHex twice
2414       {
2415         vTool.Set( curHex );
2416         if ( vTool.IsFreeFace( curFacet, &curHex ))
2417         {
2418           curHex = 0;
2419         }
2420         else
2421         {
2422           vTool.GetFaceNodes( curFacet, facetNodes );
2423           vTool.Set( curHex );
2424           curFacet = vTool.GetFaceIndex( facetNodes );
2425         }
2426       }
2427       while ( curHex )
2428       {
2429         // store a facet to split
2430         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2431         {
2432           theFacets.insert( make_pair( curHex, -1 ));
2433           break;
2434         }
2435         if ( !allHex && !theHexas.count( curHex ))
2436           break;
2437
2438         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439           theFacets.insert( make_pair( curHex, curFacet ));
2440         if ( !facetIt2isNew.second )
2441           break;
2442
2443         // remember not-to-split facets in facetsToCheck
2444         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2446         {
2447           if ( iF == curFacet && iF == oppFacet )
2448             continue;
2449           TVolumeFaceKey facetKey ( vTool, iF );
2450           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2451           pair< TFacetMap::iterator, bool > it2isnew =
2452             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453           if ( !it2isnew.second )
2454             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2455         }
2456         // pass to a volume adjacent via oppFacet
2457         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2458         {
2459           curHex = 0;
2460         }
2461         else
2462         {
2463           // get a new curFacet
2464           vTool.GetFaceNodes( oppFacet, facetNodes );
2465           vTool.Set( curHex );
2466           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2467         }
2468       }
2469     } // move in two directions from startHex via facetID
2470
2471     // Find a new startHex by facetsToCheck
2472
2473     startHex = 0;
2474     facetID  = -1;
2475     TFacetMap::iterator fIt = facetsToCheck.begin();
2476     while ( !startHex && fIt != facetsToCheck.end() )
2477     {
2478       const TElemFacets&  elemFacets = fIt->second;
2479       const SMDS_MeshElement*    hex = elemFacets.first->first;
2480       int                 splitFacet = elemFacets.first->second;
2481       int               lateralFacet = elemFacets.second;
2482       facetsToCheck.erase( fIt );
2483       fIt = facetsToCheck.begin();
2484
2485       vTool.Set( hex );
2486       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2487            curHex->GetGeomType() != SMDSGeom_HEXA )
2488         continue;
2489       if ( !allHex && !theHexas.count( curHex ))
2490         continue;
2491
2492       startHex = curHex;
2493
2494       // find a facet of startHex to split 
2495
2496       set<const SMDS_MeshNode*> lateralNodes;
2497       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498       vTool.GetFaceNodes( splitFacet,   facetNodes );
2499       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500       vTool.Set( startHex );
2501       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2502
2503       // look for a facet of startHex having common nodes with facetNodes
2504       // but not lateralFacet
2505       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2506       {
2507         if ( iF == lateralFacet )
2508           continue;
2509         int nbCommonNodes = 0;
2510         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512           nbCommonNodes += facetNodes.count( nn[ iN ]);
2513
2514         if ( nbCommonNodes >= 2 )
2515         {
2516           facetID = iF;
2517           break;
2518         }
2519       }
2520       if ( facetID < 0 )
2521         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2522     }
2523   } //   while ( startHex )
2524 }
2525
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2530
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532                                         const SMDS_MeshElement* elemInGroups,
2533                                         SMESHDS_Mesh *          aMesh)
2534 {
2535   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536   if (!groups.empty()) {
2537     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538     for ( ; grIt != groups.end(); grIt++ ) {
2539       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540       if ( group && group->Contains( elemInGroups ))
2541         group->SMDSGroup().Add( elemToAdd );
2542     }
2543   }
2544 }
2545
2546
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552                                              SMESHDS_Mesh *          aMesh)
2553 {
2554   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555   if (!groups.empty())
2556   {
2557     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558     for (; GrIt != groups.end(); GrIt++)
2559     {
2560       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561       if (!grp || grp->IsEmpty()) continue;
2562       grp->SMDSGroup().Remove(removeelem);
2563     }
2564   }
2565 }
2566
2567 //================================================================================
2568 /*!
2569  * \brief Replace elemToRm by elemToAdd in the all groups
2570  */
2571 //================================================================================
2572
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574                                             const SMDS_MeshElement* elemToAdd,
2575                                             SMESHDS_Mesh *          aMesh)
2576 {
2577   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578   if (!groups.empty()) {
2579     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580     for ( ; grIt != groups.end(); grIt++ ) {
2581       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583         group->SMDSGroup().Add( elemToAdd );
2584     }
2585   }
2586 }
2587
2588 //================================================================================
2589 /*!
2590  * \brief Replace elemToRm by elemToAdd in the all groups
2591  */
2592 //================================================================================
2593
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2595                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2596                                             SMESHDS_Mesh *                         aMesh)
2597 {
2598   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599   if (!groups.empty())
2600   {
2601     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602     for ( ; grIt != groups.end(); grIt++ ) {
2603       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605         for ( int i = 0; i < elemToAdd.size(); ++i )
2606           group->SMDSGroup().Add( elemToAdd[ i ] );
2607     }
2608   }
2609 }
2610
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose  : Cut quadrangles into triangles.
2614 //           theCrit is used to select a diagonal to cut
2615 //=======================================================================
2616
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618                                   const bool         the13Diag)
2619 {
2620   myLastCreatedElems.Clear();
2621   myLastCreatedNodes.Clear();
2622
2623   MESSAGE( "::QuadToTri()" );
2624
2625   SMESHDS_Mesh * aMesh = GetMeshDS();
2626
2627   Handle(Geom_Surface) surface;
2628   SMESH_MesherHelper   helper( *GetMesh() );
2629
2630   TIDSortedElemSet::iterator itElem;
2631   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632     const SMDS_MeshElement* elem = *itElem;
2633     if ( !elem || elem->GetType() != SMDSAbs_Face )
2634       continue;
2635     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636     if(!isquad) continue;
2637
2638     if(elem->NbNodes()==4) {
2639       // retrieve element nodes
2640       const SMDS_MeshNode* aNodes [4];
2641       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2642       int i = 0;
2643       while ( itN->more() )
2644         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2645
2646       int aShapeId = FindShape( elem );
2647       const SMDS_MeshElement* newElem1 = 0;
2648       const SMDS_MeshElement* newElem2 = 0;
2649       if ( the13Diag ) {
2650         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2652       }
2653       else {
2654         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2656       }
2657       myLastCreatedElems.Append(newElem1);
2658       myLastCreatedElems.Append(newElem2);
2659       // put a new triangle on the same shape and add to the same groups
2660       if ( aShapeId )
2661         {
2662           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2664         }
2665       AddToSameGroups( newElem1, elem, aMesh );
2666       AddToSameGroups( newElem2, elem, aMesh );
2667       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668       aMesh->RemoveElement( elem );
2669     }
2670
2671     // Quadratic quadrangle
2672
2673     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2674
2675       // get surface elem is on
2676       int aShapeId = FindShape( elem );
2677       if ( aShapeId != helper.GetSubShapeID() ) {
2678         surface.Nullify();
2679         TopoDS_Shape shape;
2680         if ( aShapeId > 0 )
2681           shape = aMesh->IndexToShape( aShapeId );
2682         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683           TopoDS_Face face = TopoDS::Face( shape );
2684           surface = BRep_Tool::Surface( face );
2685           if ( !surface.IsNull() )
2686             helper.SetSubShape( shape );
2687         }
2688       }
2689
2690       const SMDS_MeshNode* aNodes [8];
2691       const SMDS_MeshNode* inFaceNode = 0;
2692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2693       int i = 0;
2694       while ( itN->more() ) {
2695         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2698         {
2699           inFaceNode = aNodes[ i-1 ];
2700         }
2701       }
2702
2703       // find middle point for (0,1,2,3)
2704       // and create a node in this point;
2705       gp_XYZ p( 0,0,0 );
2706       if ( surface.IsNull() ) {
2707         for(i=0; i<4; i++)
2708           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2709         p /= 4;
2710       }
2711       else {
2712         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2713         gp_XY uv( 0,0 );
2714         for(i=0; i<4; i++)
2715           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2716         uv /= 4.;
2717         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2718       }
2719       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720       myLastCreatedNodes.Append(newN);
2721
2722       // create a new element
2723       const SMDS_MeshElement* newElem1 = 0;
2724       const SMDS_MeshElement* newElem2 = 0;
2725       if ( the13Diag ) {
2726         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727                                   aNodes[6], aNodes[7], newN );
2728         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729                                   newN,      aNodes[4], aNodes[5] );
2730       }
2731       else {
2732         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733                                   aNodes[7], aNodes[4], newN );
2734         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735                                   newN,      aNodes[5], aNodes[6] );
2736       }
2737       myLastCreatedElems.Append(newElem1);
2738       myLastCreatedElems.Append(newElem2);
2739       // put a new triangle on the same shape and add to the same groups
2740       if ( aShapeId )
2741         {
2742           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2744         }
2745       AddToSameGroups( newElem1, elem, aMesh );
2746       AddToSameGroups( newElem2, elem, aMesh );
2747       aMesh->RemoveElement( elem );
2748     }
2749   }
2750
2751   return true;
2752 }
2753
2754 //=======================================================================
2755 //function : getAngle
2756 //purpose  :
2757 //=======================================================================
2758
2759 double getAngle(const SMDS_MeshElement * tr1,
2760                 const SMDS_MeshElement * tr2,
2761                 const SMDS_MeshNode *    n1,
2762                 const SMDS_MeshNode *    n2)
2763 {
2764   double angle = 2. * M_PI; // bad angle
2765
2766   // get normals
2767   SMESH::Controls::TSequenceOfXYZ P1, P2;
2768   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2770     return angle;
2771   gp_Vec N1,N2;
2772   if(!tr1->IsQuadratic())
2773     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2774   else
2775     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776   if ( N1.SquareMagnitude() <= gp::Resolution() )
2777     return angle;
2778   if(!tr2->IsQuadratic())
2779     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2780   else
2781     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782   if ( N2.SquareMagnitude() <= gp::Resolution() )
2783     return angle;
2784
2785   // find the first diagonal node n1 in the triangles:
2786   // take in account a diagonal link orientation
2787   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788   for ( int t = 0; t < 2; t++ ) {
2789     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790     int i = 0, iDiag = -1;
2791     while ( it->more()) {
2792       const SMDS_MeshElement *n = it->next();
2793       if ( n == n1 || n == n2 ) {
2794         if ( iDiag < 0)
2795           iDiag = i;
2796         else {
2797           if ( i - iDiag == 1 )
2798             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2799           else
2800             nFirst[ t ] = n;
2801           break;
2802         }
2803       }
2804       i++;
2805     }
2806   }
2807   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2808     N2.Reverse();
2809
2810   angle = N1.Angle( N2 );
2811   //SCRUTE( angle );
2812   return angle;
2813 }
2814
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2819 class LinkID_Gen {
2820 public:
2821
2822   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2824   {}
2825
2826   long GetLinkID (const SMDS_MeshNode * n1,
2827                   const SMDS_MeshNode * n2) const
2828   {
2829     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2830   }
2831
2832   bool GetNodes (const long             theLinkID,
2833                  const SMDS_MeshNode* & theNode1,
2834                  const SMDS_MeshNode* & theNode2) const
2835   {
2836     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837     if ( !theNode1 ) return false;
2838     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839     if ( !theNode2 ) return false;
2840     return true;
2841   }
2842
2843 private:
2844   LinkID_Gen();
2845   const SMESHDS_Mesh* myMesh;
2846   long                myMaxID;
2847 };
2848
2849
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose  : Fuse neighbour triangles into quadrangles.
2853 //           theCrit is used to select a neighbour to fuse with.
2854 //           theMaxAngle is a max angle between element normals at which
2855 //           fusion is still performed.
2856 //=======================================================================
2857
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2859                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2860                                   const double                         theMaxAngle)
2861 {
2862   myLastCreatedElems.Clear();
2863   myLastCreatedNodes.Clear();
2864
2865   MESSAGE( "::TriToQuad()" );
2866
2867   if ( !theCrit.get() )
2868     return false;
2869
2870   SMESHDS_Mesh * aMesh = GetMeshDS();
2871
2872   // Prepare data for algo: build
2873   // 1. map of elements with their linkIDs
2874   // 2. map of linkIDs with their elements
2875
2876   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2879   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2880
2881   TIDSortedElemSet::iterator itElem;
2882   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2883   {
2884     const SMDS_MeshElement* elem = *itElem;
2885     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886     bool IsTria = ( elem->NbCornerNodes()==3 );
2887     if (!IsTria) continue;
2888
2889     // retrieve element nodes
2890     const SMDS_MeshNode* aNodes [4];
2891     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2892     int i = 0;
2893     while ( i < 3 )
2894       aNodes[ i++ ] = itN->next();
2895     aNodes[ 3 ] = aNodes[ 0 ];
2896
2897     // fill maps
2898     for ( i = 0; i < 3; i++ ) {
2899       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900       // check if elements sharing a link can be fused
2901       itLE = mapLi_listEl.find( link );
2902       if ( itLE != mapLi_listEl.end() ) {
2903         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2904           continue;
2905         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906         //if ( FindShape( elem ) != FindShape( elem2 ))
2907         //  continue; // do not fuse triangles laying on different shapes
2908         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909           continue; // avoid making badly shaped quads
2910         (*itLE).second.push_back( elem );
2911       }
2912       else {
2913         mapLi_listEl[ link ].push_back( elem );
2914       }
2915       mapEl_setLi [ elem ].insert( link );
2916     }
2917   }
2918   // Clean the maps from the links shared by a sole element, ie
2919   // links to which only one element is bound in mapLi_listEl
2920
2921   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922     int nbElems = (*itLE).second.size();
2923     if ( nbElems < 2  ) {
2924       const SMDS_MeshElement* elem = (*itLE).second.front();
2925       SMESH_TLink link = (*itLE).first;
2926       mapEl_setLi[ elem ].erase( link );
2927       if ( mapEl_setLi[ elem ].empty() )
2928         mapEl_setLi.erase( elem );
2929     }
2930   }
2931
2932   // Algo: fuse triangles into quadrangles
2933
2934   while ( ! mapEl_setLi.empty() ) {
2935     // Look for the start element:
2936     // the element having the least nb of shared links
2937     const SMDS_MeshElement* startElem = 0;
2938     int minNbLinks = 4;
2939     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940       int nbLinks = (*itEL).second.size();
2941       if ( nbLinks < minNbLinks ) {
2942         startElem = (*itEL).first;
2943         minNbLinks = nbLinks;
2944         if ( minNbLinks == 1 )
2945           break;
2946       }
2947     }
2948
2949     // search elements to fuse starting from startElem or links of elements
2950     // fused earlyer - startLinks
2951     list< SMESH_TLink > startLinks;
2952     while ( startElem || !startLinks.empty() ) {
2953       while ( !startElem && !startLinks.empty() ) {
2954         // Get an element to start, by a link
2955         SMESH_TLink linkId = startLinks.front();
2956         startLinks.pop_front();
2957         itLE = mapLi_listEl.find( linkId );
2958         if ( itLE != mapLi_listEl.end() ) {
2959           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961           for ( ; itE != listElem.end() ; itE++ )
2962             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2963               startElem = (*itE);
2964           mapLi_listEl.erase( itLE );
2965         }
2966       }
2967
2968       if ( startElem ) {
2969         // Get candidates to be fused
2970         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971         const SMESH_TLink *link12, *link13;
2972         startElem = 0;
2973         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975         ASSERT( !setLi.empty() );
2976         set< SMESH_TLink >::iterator itLi;
2977         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2978         {
2979           const SMESH_TLink & link = (*itLi);
2980           itLE = mapLi_listEl.find( link );
2981           if ( itLE == mapLi_listEl.end() )
2982             continue;
2983
2984           const SMDS_MeshElement* elem = (*itLE).second.front();
2985           if ( elem == tr1 )
2986             elem = (*itLE).second.back();
2987           mapLi_listEl.erase( itLE );
2988           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2989             continue;
2990           if ( tr2 ) {
2991             tr3 = elem;
2992             link13 = &link;
2993           }
2994           else {
2995             tr2 = elem;
2996             link12 = &link;
2997           }
2998
2999           // add other links of elem to list of links to re-start from
3000           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001           set< SMESH_TLink >::iterator it;
3002           for ( it = links.begin(); it != links.end(); it++ ) {
3003             const SMESH_TLink& link2 = (*it);
3004             if ( link2 != link )
3005               startLinks.push_back( link2 );
3006           }
3007         }
3008
3009         // Get nodes of possible quadrangles
3010         const SMDS_MeshNode *n12 [4], *n13 [4];
3011         bool Ok12 = false, Ok13 = false;
3012         const SMDS_MeshNode *linkNode1, *linkNode2;
3013         if(tr2) {
3014           linkNode1 = link12->first;
3015           linkNode2 = link12->second;
3016           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3017             Ok12 = true;
3018         }
3019         if(tr3) {
3020           linkNode1 = link13->first;
3021           linkNode2 = link13->second;
3022           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3023             Ok13 = true;
3024         }
3025
3026         // Choose a pair to fuse
3027         if ( Ok12 && Ok13 ) {
3028           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030           double aBadRate12 = getBadRate( &quad12, theCrit );
3031           double aBadRate13 = getBadRate( &quad13, theCrit );
3032           if (  aBadRate13 < aBadRate12 )
3033             Ok12 = false;
3034           else
3035             Ok13 = false;
3036         }
3037
3038         // Make quadrangles
3039         // and remove fused elems and remove links from the maps
3040         mapEl_setLi.erase( tr1 );
3041         if ( Ok12 )
3042         {
3043           mapEl_setLi.erase( tr2 );
3044           mapLi_listEl.erase( *link12 );
3045           if ( tr1->NbNodes() == 3 )
3046           {
3047             const SMDS_MeshElement* newElem = 0;
3048             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049             myLastCreatedElems.Append(newElem);
3050             AddToSameGroups( newElem, tr1, aMesh );
3051             int aShapeId = tr1->getshapeId();
3052             if ( aShapeId )
3053               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054             aMesh->RemoveElement( tr1 );
3055             aMesh->RemoveElement( tr2 );
3056           }
3057           else {
3058             vector< const SMDS_MeshNode* > N1;
3059             vector< const SMDS_MeshNode* > N2;
3060             getNodesFromTwoTria(tr1,tr2,N1,N2);
3061             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3063             // i.e. first nodes from both arrays form a new diagonal
3064             const SMDS_MeshNode* aNodes[8];
3065             aNodes[0] = N1[0];
3066             aNodes[1] = N1[1];
3067             aNodes[2] = N2[0];
3068             aNodes[3] = N2[1];
3069             aNodes[4] = N1[3];
3070             aNodes[5] = N2[5];
3071             aNodes[6] = N2[3];
3072             aNodes[7] = N1[5];
3073             const SMDS_MeshElement* newElem = 0;
3074             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3077             else
3078               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080             myLastCreatedElems.Append(newElem);
3081             AddToSameGroups( newElem, tr1, aMesh );
3082             int aShapeId = tr1->getshapeId();
3083             if ( aShapeId )
3084               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085             aMesh->RemoveElement( tr1 );
3086             aMesh->RemoveElement( tr2 );
3087             // remove middle node (9)
3088             if ( N1[4]->NbInverseElements() == 0 )
3089               aMesh->RemoveNode( N1[4] );
3090             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091               aMesh->RemoveNode( N1[6] );
3092             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093               aMesh->RemoveNode( N2[6] );
3094           }
3095         }
3096         else if ( Ok13 )
3097         {
3098           mapEl_setLi.erase( tr3 );
3099           mapLi_listEl.erase( *link13 );
3100           if ( tr1->NbNodes() == 3 ) {
3101             const SMDS_MeshElement* newElem = 0;
3102             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103             myLastCreatedElems.Append(newElem);
3104             AddToSameGroups( newElem, tr1, aMesh );
3105             int aShapeId = tr1->getshapeId();
3106             if ( aShapeId )
3107               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108             aMesh->RemoveElement( tr1 );
3109             aMesh->RemoveElement( tr3 );
3110           }
3111           else {
3112             vector< const SMDS_MeshNode* > N1;
3113             vector< const SMDS_MeshNode* > N2;
3114             getNodesFromTwoTria(tr1,tr3,N1,N2);
3115             // now we receive following N1 and N2 (using numeration as above image)
3116             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3117             // i.e. first nodes from both arrays form a new diagonal
3118             const SMDS_MeshNode* aNodes[8];
3119             aNodes[0] = N1[0];
3120             aNodes[1] = N1[1];
3121             aNodes[2] = N2[0];
3122             aNodes[3] = N2[1];
3123             aNodes[4] = N1[3];
3124             aNodes[5] = N2[5];
3125             aNodes[6] = N2[3];
3126             aNodes[7] = N1[5];
3127             const SMDS_MeshElement* newElem = 0;
3128             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3131             else
3132               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134             myLastCreatedElems.Append(newElem);
3135             AddToSameGroups( newElem, tr1, aMesh );
3136             int aShapeId = tr1->getshapeId();
3137             if ( aShapeId )
3138               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139             aMesh->RemoveElement( tr1 );
3140             aMesh->RemoveElement( tr3 );
3141             // remove middle node (9)
3142             if ( N1[4]->NbInverseElements() == 0 )
3143               aMesh->RemoveNode( N1[4] );
3144             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145               aMesh->RemoveNode( N1[6] );
3146             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147               aMesh->RemoveNode( N2[6] );
3148           }
3149         }
3150
3151         // Next element to fuse: the rejected one
3152         if ( tr3 )
3153           startElem = Ok12 ? tr3 : tr2;
3154
3155       } // if ( startElem )
3156     } // while ( startElem || !startLinks.empty() )
3157   } // while ( ! mapEl_setLi.empty() )
3158
3159   return true;
3160 }
3161
3162
3163 /*#define DUMPSO(txt) \
3164 //  cout << txt << endl;
3165 //=============================================================================
3166 //
3167 //
3168 //
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3171 {
3172 if ( i1 == i2 )
3173 return;
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3178 P[ i1 ] = P[ i2 ];
3179 P[ i2 ] = Ptmp;
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3181 }
3182
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3186 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3187 //           1 or 2 else 0.
3188 //=======================================================================
3189
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3191 int               idNodes[] )
3192 {
3193   gp_Pnt P[4];
3194   int i;
3195   for ( i = 0; i < 4; i++ ) {
3196     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3197     if ( !n ) return 0;
3198     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3199   }
3200
3201   gp_Vec V1(P[0], P[1]);
3202   gp_Vec V2(P[0], P[2]);
3203   gp_Vec V3(P[0], P[3]);
3204
3205   gp_Vec Cross1 = V1 ^ V2;
3206   gp_Vec Cross2 = V2 ^ V3;
3207
3208   i = 0;
3209   if (Cross1.Dot(Cross2) < 0)
3210   {
3211     Cross1 = V2 ^ V1;
3212     Cross2 = V1 ^ V3;
3213
3214     if (Cross1.Dot(Cross2) < 0)
3215       i = 2;
3216     else
3217       i = 1;
3218     swap ( i, i + 1, idNodes, P );
3219
3220     //     for ( int ii = 0; ii < 4; ii++ ) {
3221     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3223     //     }
3224   }
3225   return i;
3226 }
3227
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose  : Set 8 nodes of a hexahedron in a good order.
3231 //           Return success status
3232 //=======================================================================
3233
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3235                                       int               idNodes[] )
3236 {
3237   gp_Pnt P[8];
3238   int i;
3239   DUMPSO( "INPUT: ========================================");
3240   for ( i = 0; i < 8; i++ ) {
3241     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242     if ( !n ) return false;
3243     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3245   }
3246   DUMPSO( "========================================");
3247
3248
3249   set<int> faceNodes;  // ids of bottom face nodes, to be found
3250   set<int> checkedId1; // ids of tried 2-nd nodes
3251   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3253   int iMin, iLoop1 = 0;
3254
3255   // Loop to try the 2-nd nodes
3256
3257   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3258   {
3259     // Find not checked 2-nd node
3260     for ( i = 1; i < 8; i++ )
3261       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262         int id1 = idNodes[i];
3263         swap ( 1, i, idNodes, P );
3264         checkedId1.insert ( id1 );
3265         break;
3266       }
3267
3268     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269     // ie that all but meybe one (id3 which is on the same face) nodes
3270     // lay on the same side from the triangle plane.
3271
3272     bool manyInPlane = false; // more than 4 nodes lay in plane
3273     int iLoop2 = 0;
3274     while ( ++iLoop2 < 6 ) {
3275
3276       // get 1-2-3 plane coeffs
3277       Standard_Real A, B, C, D;
3278       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279       if ( N.SquareMagnitude() > gp::Resolution() )
3280       {
3281         gp_Pln pln ( P[0], N );
3282         pln.Coefficients( A, B, C, D );
3283
3284         // find the node (iMin) closest to pln
3285         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3286         set<int> idInPln;
3287         for ( i = 3; i < 8; i++ ) {
3288           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289           if ( fabs( dist[i] ) < minDist ) {
3290             minDist = fabs( dist[i] );
3291             iMin = i;
3292           }
3293           if ( fabs( dist[i] ) <= tol )
3294             idInPln.insert( idNodes[i] );
3295         }
3296
3297         // there should not be more than 4 nodes in bottom plane
3298         if ( idInPln.size() > 1 )
3299         {
3300           DUMPSO( "### idInPln.size() = " << idInPln.size());
3301           // idInPlane does not contain the first 3 nodes
3302           if ( manyInPlane || idInPln.size() == 5)
3303             return false; // all nodes in one plane
3304           manyInPlane = true;
3305
3306           // set the 1-st node to be not in plane
3307           for ( i = 3; i < 8; i++ ) {
3308             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309               DUMPSO( "### Reset 0-th node");
3310               swap( 0, i, idNodes, P );
3311               break;
3312             }
3313           }
3314
3315           // reset to re-check second nodes
3316           leastDist = DBL_MAX;
3317           faceNodes.clear();
3318           checkedId1.clear();
3319           iLoop1 = 0;
3320           break; // from iLoop2;
3321         }
3322
3323         // check that the other 4 nodes are on the same side
3324         bool sameSide = true;
3325         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326         for ( i = 3; sameSide && i < 8; i++ ) {
3327           if ( i != iMin )
3328             sameSide = ( isNeg == dist[i] <= 0.);
3329         }
3330
3331         // keep best solution
3332         if ( sameSide && minDist < leastDist ) {
3333           leastDist = minDist;
3334           faceNodes.clear();
3335           faceNodes.insert( idNodes[ 1 ] );
3336           faceNodes.insert( idNodes[ 2 ] );
3337           faceNodes.insert( idNodes[ iMin ] );
3338           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339                   << " leastDist = " << leastDist);
3340           if ( leastDist <= DBL_MIN )
3341             break;
3342         }
3343       }
3344
3345       // set next 3-d node to check
3346       int iNext = 2 + iLoop2;
3347       if ( iNext < 8 ) {
3348         DUMPSO( "Try 2-nd");
3349         swap ( 2, iNext, idNodes, P );
3350       }
3351     } // while ( iLoop2 < 6 )
3352   } // iLoop1
3353
3354   if ( faceNodes.empty() ) return false;
3355
3356   // Put the faceNodes in proper places
3357   for ( i = 4; i < 8; i++ ) {
3358     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359       // find a place to put
3360       int iTo = 1;
3361       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3362         iTo++;
3363       DUMPSO( "Set faceNodes");
3364       swap ( iTo, i, idNodes, P );
3365     }
3366   }
3367
3368
3369   // Set nodes of the found bottom face in good order
3370   DUMPSO( " Found bottom face: ");
3371   i = SortQuadNodes( theMesh, idNodes );
3372   if ( i ) {
3373     gp_Pnt Ptmp = P[ i ];
3374     P[ i ] = P[ i+1 ];
3375     P[ i+1 ] = Ptmp;
3376   }
3377   //   else
3378   //     for ( int ii = 0; ii < 4; ii++ ) {
3379   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3381   //    }
3382
3383   // Gravity center of the top and bottom faces
3384   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3386
3387   // Get direction from the bottom to the top face
3388   gp_Vec upDir ( aGCb, aGCt );
3389   Standard_Real upDirSize = upDir.Magnitude();
3390   if ( upDirSize <= gp::Resolution() ) return false;
3391   upDir / upDirSize;
3392
3393   // Assure that the bottom face normal points up
3394   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396   if ( Nb.Dot( upDir ) < 0 ) {
3397     DUMPSO( "Reverse bottom face");
3398     swap( 1, 3, idNodes, P );
3399   }
3400
3401   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402   Standard_Real minDist = DBL_MAX;
3403   for ( i = 4; i < 8; i++ ) {
3404     // projection of P[i] to the plane defined by P[0] and upDir
3405     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406     Standard_Real sqDist = P[0].SquareDistance( Pp );
3407     if ( sqDist < minDist ) {
3408       minDist = sqDist;
3409       iMin = i;
3410     }
3411   }
3412   DUMPSO( "Set 4-th");
3413   swap ( 4, iMin, idNodes, P );
3414
3415   // Set nodes of the top face in good order
3416   DUMPSO( "Sort top face");
3417   i = SortQuadNodes( theMesh, &idNodes[4] );
3418   if ( i ) {
3419     i += 4;
3420     gp_Pnt Ptmp = P[ i ];
3421     P[ i ] = P[ i+1 ];
3422     P[ i+1 ] = Ptmp;
3423   }
3424
3425   // Assure that direction of the top face normal is from the bottom face
3426   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428   if ( Nt.Dot( upDir ) < 0 ) {
3429     DUMPSO( "Reverse top face");
3430     swap( 5, 7, idNodes, P );
3431   }
3432
3433   //   DUMPSO( "OUTPUT: ========================================");
3434   //   for ( i = 0; i < 8; i++ ) {
3435   //     float *p = ugrid->GetPoint(idNodes[i]);
3436   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3437   //   }
3438
3439   return true;
3440 }*/
3441
3442 //================================================================================
3443 /*!
3444  * \brief Return nodes linked to the given one
3445  * \param theNode - the node
3446  * \param linkedNodes - the found nodes
3447  * \param type - the type of elements to check
3448  *
3449  * Medium nodes are ignored
3450  */
3451 //================================================================================
3452
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454                                        TIDSortedElemSet &   linkedNodes,
3455                                        SMDSAbs_ElementType  type )
3456 {
3457   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458   while ( elemIt->more() )
3459   {
3460     const SMDS_MeshElement* elem = elemIt->next();
3461     if(elem->GetType() == SMDSAbs_0DElement)
3462       continue;
3463
3464     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465     if ( elem->GetType() == SMDSAbs_Volume )
3466     {
3467       SMDS_VolumeTool vol( elem );
3468       while ( nodeIt->more() ) {
3469         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470         if ( theNode != n && vol.IsLinked( theNode, n ))
3471           linkedNodes.insert( n );
3472       }
3473     }
3474     else
3475     {
3476       for ( int i = 0; nodeIt->more(); ++i ) {
3477         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478         if ( n == theNode ) {
3479           int iBefore = i - 1;
3480           int iAfter  = i + 1;
3481           if ( elem->IsQuadratic() ) {
3482             int nb = elem->NbNodes() / 2;
3483             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3485           }
3486           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3488         }
3489       }
3490     }
3491   }
3492 }
3493
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose  : pulls theNode toward the center of surrounding nodes directly
3497 //           connected to that node along an element edge
3498 //=======================================================================
3499
3500 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3501                      const Handle(Geom_Surface)&          theSurface,
3502                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3503 {
3504   // find surrounding nodes
3505
3506   TIDSortedElemSet nodeSet;
3507   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3508
3509   // compute new coodrs
3510
3511   double coord[] = { 0., 0., 0. };
3512   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515     if ( theSurface.IsNull() ) { // smooth in 3D
3516       coord[0] += node->X();
3517       coord[1] += node->Y();
3518       coord[2] += node->Z();
3519     }
3520     else { // smooth in 2D
3521       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522       gp_XY* uv = theUVMap[ node ];
3523       coord[0] += uv->X();
3524       coord[1] += uv->Y();
3525     }
3526   }
3527   int nbNodes = nodeSet.size();
3528   if ( !nbNodes )
3529     return;
3530   coord[0] /= nbNodes;
3531   coord[1] /= nbNodes;
3532
3533   if ( !theSurface.IsNull() ) {
3534     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3537     coord[0] = p3d.X();
3538     coord[1] = p3d.Y();
3539     coord[2] = p3d.Z();
3540   }
3541   else
3542     coord[2] /= nbNodes;
3543
3544   // move node
3545
3546   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 }
3548
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3552 //           surrounding elements
3553 //=======================================================================
3554
3555 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3556                       const Handle(Geom_Surface)&          theSurface,
3557                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3558 {
3559   gp_XYZ aNewXYZ(0.,0.,0.);
3560   SMESH::Controls::Area anAreaFunc;
3561   double totalArea = 0.;
3562   int nbElems = 0;
3563
3564   // compute new XYZ
3565
3566   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567   while ( elemIt->more() )
3568   {
3569     const SMDS_MeshElement* elem = elemIt->next();
3570     nbElems++;
3571
3572     gp_XYZ elemCenter(0.,0.,0.);
3573     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575     int nn = elem->NbNodes();
3576     if(elem->IsQuadratic()) nn = nn/2;
3577     int i=0;
3578     //while ( itN->more() ) {
3579     while ( i<nn ) {
3580       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3581       i++;
3582       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583       aNodePoints.push_back( aP );
3584       if ( !theSurface.IsNull() ) { // smooth in 2D
3585         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586         gp_XY* uv = theUVMap[ aNode ];
3587         aP.SetCoord( uv->X(), uv->Y(), 0. );
3588       }
3589       elemCenter += aP;
3590     }
3591     double elemArea = anAreaFunc.GetValue( aNodePoints );
3592     totalArea += elemArea;
3593     elemCenter /= nn;
3594     aNewXYZ += elemCenter * elemArea;
3595   }
3596   aNewXYZ /= totalArea;
3597   if ( !theSurface.IsNull() ) {
3598     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3600   }
3601
3602   // move node
3603
3604   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 }
3606
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose  : return UV of closest projection
3610 //=======================================================================
3611
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613                           const gp_Pnt&     point,
3614                           gp_XY &           result)
3615 {
3616   projector.Perform( point );
3617   if ( projector.IsDone() ) {
3618     double u, v, minVal = DBL_MAX;
3619     for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621       if ( projector.SquareDistance( i ) < minVal ) {
3622         minVal = projector.SquareDistance( i );
3623 #else
3624       if ( projector.Value( i ) < minVal ) {
3625         minVal = projector.Value( i );
3626 #endif
3627         projector.Point( i ).Parameter( u, v );
3628       }
3629     result.SetCoord( u, v );
3630     return true;
3631   }
3632   return false;
3633 }
3634
3635 //=======================================================================
3636 //function : Smooth
3637 //purpose  : Smooth theElements during theNbIterations or until a worst
3638 //           element has aspect ratio <= theTgtAspectRatio.
3639 //           Aspect Ratio varies in range [1.0, inf].
3640 //           If theElements is empty, the whole mesh is smoothed.
3641 //           theFixedNodes contains additionally fixed nodes. Nodes built
3642 //           on edges and boundary nodes are always fixed.
3643 //=======================================================================
3644
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3646                                set<const SMDS_MeshNode*> & theFixedNodes,
3647                                const SmoothMethod          theSmoothMethod,
3648                                const int                   theNbIterations,
3649                                double                      theTgtAspectRatio,
3650                                const bool                  the2D)
3651 {
3652   myLastCreatedElems.Clear();
3653   myLastCreatedNodes.Clear();
3654
3655   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3656
3657   if ( theTgtAspectRatio < 1.0 )
3658     theTgtAspectRatio = 1.0;
3659
3660   const double disttol = 1.e-16;
3661
3662   SMESH::Controls::AspectRatio aQualityFunc;
3663
3664   SMESHDS_Mesh* aMesh = GetMeshDS();
3665
3666   if ( theElems.empty() ) {
3667     // add all faces to theElems
3668     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669     while ( fIt->more() ) {
3670       const SMDS_MeshElement* face = fIt->next();
3671       theElems.insert( theElems.end(), face );
3672     }
3673   }
3674   // get all face ids theElems are on
3675   set< int > faceIdSet;
3676   TIDSortedElemSet::iterator itElem;
3677   if ( the2D )
3678     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679       int fId = FindShape( *itElem );
3680       // check that corresponding submesh exists and a shape is face
3681       if (fId &&
3682           faceIdSet.find( fId ) == faceIdSet.end() &&
3683           aMesh->MeshElements( fId )) {
3684         TopoDS_Shape F = aMesh->IndexToShape( fId );
3685         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686           faceIdSet.insert( fId );
3687       }
3688     }
3689   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3690
3691   // ===============================================
3692   // smooth elements on each TopoDS_Face separately
3693   // ===============================================
3694
3695   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696   for ( ; fId != faceIdSet.rend(); ++fId ) {
3697     // get face surface and submesh
3698     Handle(Geom_Surface) surface;
3699     SMESHDS_SubMesh* faceSubMesh = 0;
3700     TopoDS_Face face;
3701     double fToler2 = 0, f,l;
3702     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703     bool isUPeriodic = false, isVPeriodic = false;
3704     if ( *fId ) {
3705       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706       surface = BRep_Tool::Surface( face );
3707       faceSubMesh = aMesh->MeshElements( *fId );
3708       fToler2 = BRep_Tool::Tolerance( face );
3709       fToler2 *= fToler2 * 10.;
3710       isUPeriodic = surface->IsUPeriodic();
3711       if ( isUPeriodic )
3712         surface->UPeriod();
3713       isVPeriodic = surface->IsVPeriodic();
3714       if ( isVPeriodic )
3715         surface->VPeriod();
3716       surface->Bounds( u1, u2, v1, v2 );
3717     }
3718     // ---------------------------------------------------------
3719     // for elements on a face, find movable and fixed nodes and
3720     // compute UV for them
3721     // ---------------------------------------------------------
3722     bool checkBoundaryNodes = false;
3723     bool isQuadratic = false;
3724     set<const SMDS_MeshNode*> setMovableNodes;
3725     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727     list< const SMDS_MeshElement* > elemsOnFace;
3728
3729     Extrema_GenExtPS projector;
3730     GeomAdaptor_Surface surfAdaptor;
3731     if ( !surface.IsNull() ) {
3732       surfAdaptor.Load( surface );
3733       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3734     }
3735     int nbElemOnFace = 0;
3736     itElem = theElems.begin();
3737     // loop on not yet smoothed elements: look for elems on a face
3738     while ( itElem != theElems.end() ) {
3739       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740         break; // all elements found
3741
3742       const SMDS_MeshElement* elem = *itElem;
3743       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3745         ++itElem;
3746         continue;
3747       }
3748       elemsOnFace.push_back( elem );
3749       theElems.erase( itElem++ );
3750       nbElemOnFace++;
3751
3752       if ( !isQuadratic )
3753         isQuadratic = elem->IsQuadratic();
3754
3755       // get movable nodes of elem
3756       const SMDS_MeshNode* node;
3757       SMDS_TypeOfPosition posType;
3758       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759       int nn = 0, nbn =  elem->NbNodes();
3760       if(elem->IsQuadratic())
3761         nbn = nbn/2;
3762       while ( nn++ < nbn ) {
3763         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764         const SMDS_PositionPtr& pos = node->GetPosition();
3765         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766         if (posType != SMDS_TOP_EDGE &&
3767             posType != SMDS_TOP_VERTEX &&
3768             theFixedNodes.find( node ) == theFixedNodes.end())
3769         {
3770           // check if all faces around the node are on faceSubMesh
3771           // because a node on edge may be bound to face
3772           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773           bool all = true;
3774           if ( faceSubMesh ) {
3775             while ( eIt->more() && all ) {
3776               const SMDS_MeshElement* e = eIt->next();
3777               all = faceSubMesh->Contains( e );
3778             }
3779           }
3780           if ( all )
3781             setMovableNodes.insert( node );
3782           else
3783             checkBoundaryNodes = true;
3784         }
3785         if ( posType == SMDS_TOP_3DSPACE )
3786           checkBoundaryNodes = true;
3787       }
3788
3789       if ( surface.IsNull() )
3790         continue;
3791
3792       // get nodes to check UV
3793       list< const SMDS_MeshNode* > uvCheckNodes;
3794       itN = elem->nodesIterator();
3795       nn = 0; nbn =  elem->NbNodes();
3796       if(elem->IsQuadratic())
3797         nbn = nbn/2;
3798       while ( nn++ < nbn ) {
3799         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800         if ( uvMap.find( node ) == uvMap.end() )
3801           uvCheckNodes.push_back( node );
3802         // add nodes of elems sharing node
3803         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804         //         while ( eIt->more() ) {
3805         //           const SMDS_MeshElement* e = eIt->next();
3806         //           if ( e != elem ) {
3807         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808         //             while ( nIt->more() ) {
3809         //               const SMDS_MeshNode* n =
3810         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3811         //               if ( uvMap.find( n ) == uvMap.end() )
3812         //                 uvCheckNodes.push_back( n );
3813         //             }
3814         //           }
3815         //         }
3816       }
3817       // check UV on face
3818       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819       for ( ; n != uvCheckNodes.end(); ++n ) {
3820         node = *n;
3821         gp_XY uv( 0, 0 );
3822         const SMDS_PositionPtr& pos = node->GetPosition();
3823         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3824         // get existing UV
3825         switch ( posType ) {
3826         case SMDS_TOP_FACE: {
3827           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3829           break;
3830         }
3831         case SMDS_TOP_EDGE: {
3832           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833           Handle(Geom2d_Curve) pcurve;
3834           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836           if ( !pcurve.IsNull() ) {
3837             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838             uv = pcurve->Value( u ).XY();
3839           }
3840           break;
3841         }
3842         case SMDS_TOP_VERTEX: {
3843           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3846           break;
3847         }
3848         default:;
3849         }
3850         // check existing UV
3851         bool project = true;
3852         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853         double dist1 = DBL_MAX, dist2 = 0;
3854         if ( posType != SMDS_TOP_3DSPACE ) {
3855           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856           project = dist1 > fToler2;
3857         }
3858         if ( project ) { // compute new UV
3859           gp_XY newUV;
3860           if ( !getClosestUV( projector, pNode, newUV )) {
3861             MESSAGE("Node Projection Failed " << node);
3862           }
3863           else {
3864             if ( isUPeriodic )
3865               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3866             if ( isVPeriodic )
3867               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3868             // check new UV
3869             if ( posType != SMDS_TOP_3DSPACE )
3870               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871             if ( dist2 < dist1 )
3872               uv = newUV;
3873           }
3874         }
3875         // store UV in the map
3876         listUV.push_back( uv );
3877         uvMap.insert( make_pair( node, &listUV.back() ));
3878       }
3879     } // loop on not yet smoothed elements
3880
3881     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882       checkBoundaryNodes = true;
3883
3884     // fix nodes on mesh boundary
3885
3886     if ( checkBoundaryNodes ) {
3887       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888       map< SMESH_TLink, int >::iterator link_nb;
3889       // put all elements links to linkNbMap
3890       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892         const SMDS_MeshElement* elem = (*elemIt);
3893         int nbn =  elem->NbCornerNodes();
3894         // loop on elem links: insert them in linkNbMap
3895         for ( int iN = 0; iN < nbn; ++iN ) {
3896           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898           SMESH_TLink link( n1, n2 );
3899           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3900           link_nb->second++;
3901         }
3902       }
3903       // remove nodes that are in links encountered only once from setMovableNodes
3904       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905         if ( link_nb->second == 1 ) {
3906           setMovableNodes.erase( link_nb->first.node1() );
3907           setMovableNodes.erase( link_nb->first.node2() );
3908         }
3909       }
3910     }
3911
3912     // -----------------------------------------------------
3913     // for nodes on seam edge, compute one more UV ( uvMap2 );
3914     // find movable nodes linked to nodes on seam and which
3915     // are to be smoothed using the second UV ( uvMap2 )
3916     // -----------------------------------------------------
3917
3918     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919     if ( !surface.IsNull() ) {
3920       TopExp_Explorer eExp( face, TopAbs_EDGE );
3921       for ( ; eExp.More(); eExp.Next() ) {
3922         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923         if ( !BRep_Tool::IsClosed( edge, face ))
3924           continue;
3925         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926         if ( !sm ) continue;
3927         // find out which parameter varies for a node on seam
3928         double f,l;
3929         gp_Pnt2d uv1, uv2;
3930         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931         if ( pcurve.IsNull() ) continue;
3932         uv1 = pcurve->Value( f );
3933         edge.Reverse();
3934         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935         if ( pcurve.IsNull() ) continue;
3936         uv2 = pcurve->Value( f );
3937         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3938         // assure uv1 < uv2
3939         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3941         }
3942         // get nodes on seam and its vertices
3943         list< const SMDS_MeshNode* > seamNodes;
3944         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945         while ( nSeamIt->more() ) {
3946           const SMDS_MeshNode* node = nSeamIt->next();
3947           if ( !isQuadratic || !IsMedium( node ))
3948             seamNodes.push_back( node );
3949         }
3950         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951         for ( ; vExp.More(); vExp.Next() ) {
3952           sm = aMesh->MeshElements( vExp.Current() );
3953           if ( sm ) {
3954             nSeamIt = sm->GetNodes();
3955             while ( nSeamIt->more() )
3956               seamNodes.push_back( nSeamIt->next() );
3957           }
3958         }
3959         // loop on nodes on seam
3960         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962           const SMDS_MeshNode* nSeam = *noSeIt;
3963           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964           if ( n_uv == uvMap.end() )
3965             continue;
3966           // set the first UV
3967           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968           // set the second UV
3969           listUV.push_back( *n_uv->second );
3970           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971           if ( uvMap2.empty() )
3972             uvMap2 = uvMap; // copy the uvMap contents
3973           uvMap2[ nSeam ] = &listUV.back();
3974
3975           // collect movable nodes linked to ones on seam in nodesNearSeam
3976           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977           while ( eIt->more() ) {
3978             const SMDS_MeshElement* e = eIt->next();
3979             int nbUseMap1 = 0, nbUseMap2 = 0;
3980             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981             int nn = 0, nbn =  e->NbNodes();
3982             if(e->IsQuadratic()) nbn = nbn/2;
3983             while ( nn++ < nbn )
3984             {
3985               const SMDS_MeshNode* n =
3986                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3987               if (n == nSeam ||
3988                   setMovableNodes.find( n ) == setMovableNodes.end() )
3989                 continue;
3990               // add only nodes being closer to uv2 than to uv1
3991               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992                            0.5 * ( n->Y() + nSeam->Y() ),
3993                            0.5 * ( n->Z() + nSeam->Z() ));
3994               gp_XY uv;
3995               getClosestUV( projector, pMid, uv );
3996               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997                 nodesNearSeam.insert( n );
3998                 nbUseMap2++;
3999               }
4000               else
4001                 nbUseMap1++;
4002             }
4003             // for centroidalSmooth all element nodes must
4004             // be on one side of a seam
4005             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4007               nn = 0;
4008               while ( nn++ < nbn ) {
4009                 const SMDS_MeshNode* n =
4010                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4011                 setMovableNodes.erase( n );
4012               }
4013             }
4014           }
4015         } // loop on nodes on seam
4016       } // loop on edge of a face
4017     } // if ( !face.IsNull() )
4018
4019     if ( setMovableNodes.empty() ) {
4020       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021       continue; // goto next face
4022     }
4023
4024     // -------------
4025     // SMOOTHING //
4026     // -------------
4027
4028     int it = -1;
4029     double maxRatio = -1., maxDisplacement = -1.;
4030     set<const SMDS_MeshNode*>::iterator nodeToMove;
4031     for ( it = 0; it < theNbIterations; it++ ) {
4032       maxDisplacement = 0.;
4033       nodeToMove = setMovableNodes.begin();
4034       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035         const SMDS_MeshNode* node = (*nodeToMove);
4036         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037
4038         // smooth
4039         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040         if ( theSmoothMethod == LAPLACIAN )
4041           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4042         else
4043           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4044
4045         // node displacement
4046         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048         if ( aDispl > maxDisplacement )
4049           maxDisplacement = aDispl;
4050       }
4051       // no node movement => exit
4052       //if ( maxDisplacement < 1.e-16 ) {
4053       if ( maxDisplacement < disttol ) {
4054         MESSAGE("-- no node movement --");
4055         break;
4056       }
4057
4058       // check elements quality
4059       maxRatio  = 0;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062         const SMDS_MeshElement* elem = (*elemIt);
4063         if ( !elem || elem->GetType() != SMDSAbs_Face )
4064           continue;
4065         SMESH::Controls::TSequenceOfXYZ aPoints;
4066         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067           double aValue = aQualityFunc.GetValue( aPoints );
4068           if ( aValue > maxRatio )
4069             maxRatio = aValue;
4070         }
4071       }
4072       if ( maxRatio <= theTgtAspectRatio ) {
4073         MESSAGE("-- quality achived --");
4074         break;
4075       }
4076       if (it+1 == theNbIterations) {
4077         MESSAGE("-- Iteration limit exceeded --");
4078       }
4079     } // smoothing iterations
4080
4081     MESSAGE(" Face id: " << *fId <<
4082             " Nb iterstions: " << it <<
4083             " Displacement: " << maxDisplacement <<
4084             " Aspect Ratio " << maxRatio);
4085
4086     // ---------------------------------------
4087     // new nodes positions are computed,
4088     // record movement in DS and set new UV
4089     // ---------------------------------------
4090     nodeToMove = setMovableNodes.begin();
4091     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095       if ( node_uv != uvMap.end() ) {
4096         gp_XY* uv = node_uv->second;
4097         node->SetPosition
4098           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4099       }
4100     }
4101
4102     // move medium nodes of quadratic elements
4103     if ( isQuadratic )
4104     {
4105       SMESH_MesherHelper helper( *GetMesh() );
4106       helper.SetSubShape( face );
4107       vector<const SMDS_MeshNode*> nodes;
4108       bool checkUV;
4109       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4111       {
4112         const SMDS_MeshElement* QF = *elemIt;
4113         if ( QF->IsQuadratic() )
4114         {
4115           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116                         SMDS_MeshElement::iterator() );
4117           nodes.push_back( nodes[0] );
4118           gp_Pnt xyz;
4119           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4120           {
4121             if ( !surface.IsNull() )
4122             {
4123               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4126               xyz = surface->Value( uv.X(), uv.Y() );
4127             }
4128             else {
4129               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4130             }
4131             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132               // we have to move a medium node
4133               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4134           }
4135         }
4136       }
4137     }
4138
4139   } // loop on face ids
4140
4141 }
4142
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose  : Return true if normal of prevNodes is not co-directied with
4146 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 //           iNotSame is where prevNodes and nextNodes are different.
4148 //           If result is true then future volume orientation is OK
4149 //=======================================================================
4150
4151 static bool isReverse(const SMDS_MeshElement*             face,
4152                       const vector<const SMDS_MeshNode*>& prevNodes,
4153                       const vector<const SMDS_MeshNode*>& nextNodes,
4154                       const int                           iNotSame)
4155 {
4156
4157   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159   gp_XYZ extrDir( pN - pP ), faceNorm;
4160   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4161
4162   return faceNorm * extrDir < 0.0;
4163 }
4164
4165 //=======================================================================
4166 /*!
4167  * \brief Create elements by sweeping an element
4168  * \param elem - element to sweep
4169  * \param newNodesItVec - nodes generated from each node of the element
4170  * \param newElems - generated elements
4171  * \param nbSteps - number of sweeping steps
4172  * \param srcElements - to append elem for each generated element
4173  */
4174 //=======================================================================
4175
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4177                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178                                     list<const SMDS_MeshElement*>&        newElems,
4179                                     const int                             nbSteps,
4180                                     SMESH_SequenceOfElemPtr&              srcElements)
4181 {
4182   //MESSAGE("sweepElement " << nbSteps);
4183   SMESHDS_Mesh* aMesh = GetMeshDS();
4184
4185   const int           nbNodes = elem->NbNodes();
4186   const int         nbCorners = elem->NbCornerNodes();
4187   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188                                                           polyhedron creation !!! */
4189   // Loop on elem nodes:
4190   // find new nodes and detect same nodes indices
4191   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4195
4196   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197   vector<int> sames(nbNodes);
4198   vector<bool> isSingleNode(nbNodes);
4199
4200   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4202     const SMDS_MeshNode*                         node = nnIt->first;
4203     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204     if ( listNewNodes.empty() )
4205       return;
4206
4207     itNN   [ iNode ] = listNewNodes.begin();
4208     prevNod[ iNode ] = node;
4209     nextNod[ iNode ] = listNewNodes.front();
4210
4211     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212                                                              corner node of linear */
4213     if ( prevNod[ iNode ] != nextNod [ iNode ])
4214       nbDouble += !isSingleNode[iNode];
4215
4216     if( iNode < nbCorners ) { // check corners only
4217       if ( prevNod[ iNode ] == nextNod [ iNode ])
4218         sames[nbSame++] = iNode;
4219       else
4220         iNotSameNode = iNode;
4221     }
4222   }
4223
4224   if ( nbSame == nbNodes || nbSame > 2) {
4225     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4226     return;
4227   }
4228
4229   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4230   {
4231     // fix nodes order to have bottom normal external
4232     if ( baseType == SMDSEntity_Polygon )
4233     {
4234       std::reverse( itNN.begin(), itNN.end() );
4235       std::reverse( prevNod.begin(), prevNod.end() );
4236       std::reverse( midlNod.begin(), midlNod.end() );
4237       std::reverse( nextNod.begin(), nextNod.end() );
4238       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4239     }
4240     else
4241     {
4242       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243       SMDS_MeshCell::applyInterlace( ind, itNN );
4244       SMDS_MeshCell::applyInterlace( ind, prevNod );
4245       SMDS_MeshCell::applyInterlace( ind, nextNod );
4246       SMDS_MeshCell::applyInterlace( ind, midlNod );
4247       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4248       if ( nbSame > 0 )
4249       {
4250         sames[nbSame] = iNotSameNode;
4251         for ( int j = 0; j <= nbSame; ++j )
4252           for ( size_t i = 0; i < ind.size(); ++i )
4253             if ( ind[i] == sames[j] )
4254             {
4255               sames[j] = i;
4256               break;
4257             }
4258         iNotSameNode = sames[nbSame];
4259       }
4260     }
4261   }
4262
4263   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4264   if ( nbSame > 0 ) {
4265     iSameNode    = sames[ nbSame-1 ];
4266     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4268     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4269   }
4270
4271   // make new elements
4272   for (int iStep = 0; iStep < nbSteps; iStep++ )
4273   {
4274     // get next nodes
4275     for ( iNode = 0; iNode < nbNodes; iNode++ )
4276     {
4277       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278       nextNod[ iNode ] = *itNN[ iNode ]++;
4279     }
4280
4281     SMDS_MeshElement* aNewElem = 0;
4282     /*if(!elem->IsPoly())*/ {
4283       switch ( baseType ) {
4284       case SMDSEntity_0D:
4285       case SMDSEntity_Node: { // sweep NODE
4286         if ( nbSame == 0 ) {
4287           if ( isSingleNode[0] )
4288             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4289           else
4290             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4291         }
4292         else
4293           return;
4294         break;
4295       }
4296       case SMDSEntity_Edge: { // sweep EDGE
4297         if ( nbDouble == 0 )
4298         {
4299           if ( nbSame == 0 ) // ---> quadrangle
4300             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301                                       nextNod[ 1 ], nextNod[ 0 ] );
4302           else               // ---> triangle
4303             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304                                       nextNod[ iNotSameNode ] );
4305         }
4306         else                 // ---> polygon
4307         {
4308           vector<const SMDS_MeshNode*> poly_nodes;
4309           poly_nodes.push_back( prevNod[0] );
4310           poly_nodes.push_back( prevNod[1] );
4311           if ( prevNod[1] != nextNod[1] )
4312           {
4313             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314             poly_nodes.push_back( nextNod[1] );
4315           }
4316           if ( prevNod[0] != nextNod[0] )
4317           {
4318             poly_nodes.push_back( nextNod[0] );
4319             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4320           }
4321           switch ( poly_nodes.size() ) {
4322           case 3:
4323             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4324             break;
4325           case 4:
4326             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4328             break;
4329           default:
4330             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4331           }
4332         }
4333         break;
4334       }
4335       case SMDSEntity_Triangle: // TRIANGLE --->
4336         {
4337           if ( nbDouble > 0 ) break;
4338           if ( nbSame == 0 )       // ---> pentahedron
4339             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4341
4342           else if ( nbSame == 1 )  // ---> pyramid
4343             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4345                                          nextNod[ iSameNode ]);
4346
4347           else // 2 same nodes:       ---> tetrahedron
4348             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349                                          nextNod[ iNotSameNode ]);
4350           break;
4351         }
4352       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4353         {
4354           if ( nbSame == 2 )
4355             return;
4356           if ( nbDouble+nbSame == 2 )
4357           {
4358             if(nbSame==0) {      // ---> quadratic quadrangle
4359               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4361             }
4362             else { //(nbSame==1) // ---> quadratic triangle
4363               if(sames[0]==2) {
4364                 return; // medium node on axis
4365               }
4366               else if(sames[0]==0)
4367                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368                                           nextNod[2], midlNod[1], prevNod[2]);
4369               else // sames[0]==1
4370                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371                                           midlNod[0], nextNod[2], prevNod[2]);
4372             }
4373           }
4374           else if ( nbDouble == 3 )
4375           {
4376             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4377               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4379             }
4380           }
4381           else
4382             return;
4383           break;
4384         }
4385       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386         if ( nbDouble > 0 ) break;
4387
4388         if ( nbSame == 0 )       // ---> hexahedron
4389           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4391
4392         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4395                                        nextNod[ iSameNode ]);
4396           newElems.push_back( aNewElem );
4397           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4398                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4400         }
4401         else if ( nbSame == 2 ) { // ---> pentahedron
4402           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403             // iBeforeSame is same too
4404             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4406                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4407           else
4408             // iAfterSame is same too
4409             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4410                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4412         }
4413         break;
4414       }
4415       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4416       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4417         if ( nbDouble+nbSame != 3 ) break;
4418         if(nbSame==0) {
4419           // --->  pentahedron with 15 nodes
4420           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421                                        nextNod[0], nextNod[1], nextNod[2],
4422                                        prevNod[3], prevNod[4], prevNod[5],
4423                                        nextNod[3], nextNod[4], nextNod[5],
4424                                        midlNod[0], midlNod[1], midlNod[2]);
4425         }
4426         else if(nbSame==1) {
4427           // --->  2d order pyramid of 13 nodes
4428           int apex = iSameNode;
4429           int i0 = ( apex + 1 ) % nbCorners;
4430           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4431           int i0a = apex + 3;
4432           int i1a = i1 + 3;
4433           int i01 = i0 + 3;
4434           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435                                       nextNod[i0], nextNod[i1], prevNod[apex],
4436                                       prevNod[i01], midlNod[i0],
4437                                       nextNod[i01], midlNod[i1],
4438                                       prevNod[i1a], prevNod[i0a],
4439                                       nextNod[i0a], nextNod[i1a]);
4440         }
4441         else if(nbSame==2) {
4442           // --->  2d order tetrahedron of 10 nodes
4443           int n1 = iNotSameNode;
4444           int n2 = ( n1 + 1             ) % nbCorners;
4445           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4446           int n12 = n1 + 3;
4447           int n23 = n2 + 3;
4448           int n31 = n3 + 3;
4449           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450                                        prevNod[n12], prevNod[n23], prevNod[n31],
4451                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4452         }
4453         break;
4454       }
4455       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4456         if( nbSame == 0 ) {
4457           if ( nbDouble != 4 ) break;
4458           // --->  hexahedron with 20 nodes
4459           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4464         }
4465         else if(nbSame==1) {
4466           // ---> pyramid + pentahedron - can not be created since it is needed
4467           // additional middle node at the center of face
4468           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4469           return;
4470         }
4471         else if( nbSame == 2 ) {
4472           if ( nbDouble != 2 ) break;
4473           // --->  2d order Pentahedron with 15 nodes
4474           int n1,n2,n4,n5;
4475           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476             // iBeforeSame is same too
4477             n1 = iBeforeSame;
4478             n2 = iOpposSame;
4479             n4 = iSameNode;
4480             n5 = iAfterSame;
4481           }
4482           else {
4483             // iAfterSame is same too
4484             n1 = iSameNode;
4485             n2 = iBeforeSame;
4486             n4 = iAfterSame;
4487             n5 = iOpposSame;
4488           }
4489           int n12 = n2 + 4;
4490           int n45 = n4 + 4;
4491           int n14 = n1 + 4;
4492           int n25 = n5 + 4;
4493           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494                                        prevNod[n4], prevNod[n5], nextNod[n5],
4495                                        prevNod[n12], midlNod[n2], nextNod[n12],
4496                                        prevNod[n45], midlNod[n5], nextNod[n45],
4497                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4498         }
4499         break;
4500       }
4501       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4502
4503         if( nbSame == 0 && nbDouble == 9 ) {
4504           // --->  tri-quadratic hexahedron with 27 nodes
4505           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510                                        prevNod[8], // bottom center
4511                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512                                        nextNod[8], // top center
4513                                        midlNod[8]);// elem center
4514         }
4515         else
4516         {
4517           return;
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Polygon: { // sweep POLYGON
4522
4523         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524           // --->  hexagonal prism
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526                                        prevNod[3], prevNod[4], prevNod[5],
4527                                        nextNod[0], nextNod[1], nextNod[2],
4528                                        nextNod[3], nextNod[4], nextNod[5]);
4529         }
4530         break;
4531       }
4532       case SMDSEntity_Ball:
4533         return;
4534
4535       default:
4536         break;
4537       }
4538     }
4539
4540     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4541     {
4542       if ( baseType != SMDSEntity_Polygon )
4543       {
4544         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545         SMDS_MeshCell::applyInterlace( ind, prevNod );
4546         SMDS_MeshCell::applyInterlace( ind, nextNod );
4547         SMDS_MeshCell::applyInterlace( ind, midlNod );
4548         SMDS_MeshCell::applyInterlace( ind, itNN );
4549         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4551       }
4552       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553       vector<int> quantities (nbNodes + 2);
4554       polyedre_nodes.clear();
4555       quantities.clear();
4556
4557       // bottom of prism
4558       for (int inode = 0; inode < nbNodes; inode++)
4559         polyedre_nodes.push_back( prevNod[inode] );
4560       quantities.push_back( nbNodes );
4561
4562       // top of prism
4563       polyedre_nodes.push_back( nextNod[0] );
4564       for (int inode = nbNodes; inode-1; --inode )
4565         polyedre_nodes.push_back( nextNod[inode-1] );
4566       quantities.push_back( nbNodes );
4567
4568       // side faces
4569       for (int iface = 0; iface < nbNodes; iface++)
4570       {
4571         const int prevNbNodes = polyedre_nodes.size();
4572         int inextface = (iface+1) % nbNodes;
4573         polyedre_nodes.push_back( prevNod[inextface] );
4574         polyedre_nodes.push_back( prevNod[iface] );
4575         if ( prevNod[iface] != nextNod[iface] )
4576         {
4577           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578           polyedre_nodes.push_back( nextNod[iface] );
4579         }
4580         if ( prevNod[inextface] != nextNod[inextface] )
4581         {
4582           polyedre_nodes.push_back( nextNod[inextface] );
4583           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4584         }
4585         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586         if ( nbFaceNodes > 2 )
4587           quantities.push_back( nbFaceNodes );
4588         else // degenerated face
4589           polyedre_nodes.resize( prevNbNodes );
4590       }
4591       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4592     }
4593
4594     if ( aNewElem ) {
4595       newElems.push_back( aNewElem );
4596       myLastCreatedElems.Append(aNewElem);
4597       srcElements.Append( elem );
4598     }
4599
4600     // set new prev nodes
4601     for ( iNode = 0; iNode < nbNodes; iNode++ )
4602       prevNod[ iNode ] = nextNod[ iNode ];
4603
4604   } // for steps
4605 }
4606
4607 //=======================================================================
4608 /*!
4609  * \brief Create 1D and 2D elements around swept elements
4610  * \param mapNewNodes - source nodes and ones generated from them
4611  * \param newElemsMap - source elements and ones generated from them
4612  * \param elemNewNodesMap - nodes generated from each node of each element
4613  * \param elemSet - all swept elements
4614  * \param nbSteps - number of sweeping steps
4615  * \param srcElements - to append elem for each generated element
4616  */
4617 //=======================================================================
4618
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4620                                   TTElemOfElemListMap &    newElemsMap,
4621                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4622                                   TIDSortedElemSet&        elemSet,
4623                                   const int                nbSteps,
4624                                   SMESH_SequenceOfElemPtr& srcElements)
4625 {
4626   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627   SMESHDS_Mesh* aMesh = GetMeshDS();
4628
4629   // Find nodes belonging to only one initial element - sweep them into edges.
4630
4631   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632   for ( ; nList != mapNewNodes.end(); nList++ )
4633   {
4634     const SMDS_MeshNode* node =
4635       static_cast<const SMDS_MeshNode*>( nList->first );
4636     if ( newElemsMap.count( node ))
4637       continue; // node was extruded into edge
4638     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639     int nbInitElems = 0;
4640     const SMDS_MeshElement* el = 0;
4641     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642     while ( eIt->more() && nbInitElems < 2 ) {
4643       el = eIt->next();
4644       SMDSAbs_ElementType type = el->GetType();
4645       if ( type == SMDSAbs_Volume || type < highType ) continue;
4646       if ( type > highType ) {
4647         nbInitElems = 0;
4648         highType = type;
4649       }
4650       nbInitElems += elemSet.count(el);
4651     }
4652     if ( nbInitElems < 2 ) {
4653       bool NotCreateEdge = el && el->IsMediumNode(node);
4654       if(!NotCreateEdge) {
4655         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656         list<const SMDS_MeshElement*> newEdges;
4657         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4658       }
4659     }
4660   }
4661
4662   // Make a ceiling for each element ie an equal element of last new nodes.
4663   // Find free links of faces - make edges and sweep them into faces.
4664
4665   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4666   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4668   {
4669     const SMDS_MeshElement* elem = itElem->first;
4670     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4671
4672     if(itElem->second.size()==0) continue;
4673
4674     const bool isQuadratic = elem->IsQuadratic();
4675
4676     if ( elem->GetType() == SMDSAbs_Edge ) {
4677       // create a ceiling edge
4678       if ( !isQuadratic ) {
4679         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680                                vecNewNodes[ 1 ]->second.back())) {
4681           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682                                                    vecNewNodes[ 1 ]->second.back()));
4683           srcElements.Append( elem );
4684         }
4685       }
4686       else {
4687         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688                                vecNewNodes[ 1 ]->second.back(),
4689                                vecNewNodes[ 2 ]->second.back())) {
4690           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691                                                    vecNewNodes[ 1 ]->second.back(),
4692                                                    vecNewNodes[ 2 ]->second.back()));
4693           srcElements.Append( elem );
4694         }
4695       }
4696     }
4697     if ( elem->GetType() != SMDSAbs_Face )
4698       continue;
4699
4700     bool hasFreeLinks = false;
4701
4702     TIDSortedElemSet avoidSet;
4703     avoidSet.insert( elem );
4704
4705     set<const SMDS_MeshNode*> aFaceLastNodes;
4706     int iNode, nbNodes = vecNewNodes.size();
4707     if ( !isQuadratic ) {
4708       // loop on the face nodes
4709       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711         // look for free links of the face
4712         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715         // check if a link n1-n2 is free
4716         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717           hasFreeLinks = true;
4718           // make a new edge and a ceiling for a new edge
4719           const SMDS_MeshElement* edge;
4720           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722             srcElements.Append( myLastCreatedElems.Last() );
4723           }
4724           n1 = vecNewNodes[ iNode ]->second.back();
4725           n2 = vecNewNodes[ iNext ]->second.back();
4726           if ( !aMesh->FindEdge( n1, n2 )) {
4727             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728             srcElements.Append( edge );
4729           }
4730         }
4731       }
4732     }
4733     else { // elem is quadratic face
4734       int nbn = nbNodes/2;
4735       for ( iNode = 0; iNode < nbn; iNode++ ) {
4736         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741         // check if a link is free
4742         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745           hasFreeLinks = true;
4746           // make an edge and a ceiling for a new edge
4747           // find medium node
4748           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750             srcElements.Append( elem );
4751           }
4752           n1 = vecNewNodes[ iNode ]->second.back();
4753           n2 = vecNewNodes[ iNext ]->second.back();
4754           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757             srcElements.Append( elem );
4758           }
4759         }
4760       }
4761       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4763       }
4764     }
4765
4766     // sweep free links into faces
4767
4768     if ( hasFreeLinks )  {
4769       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4771
4772       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4777       }
4778       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4779         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4781       }
4782       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784         std::advance( v, volNb );
4785         // find indices of free faces of a volume and their source edges
4786         list< int > freeInd;
4787         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789         int iF, nbF = vTool.NbFaces();
4790         for ( iF = 0; iF < nbF; iF ++ ) {
4791           if (vTool.IsFreeFace( iF ) &&
4792               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793               initNodeSet != faceNodeSet) // except an initial face
4794           {
4795             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4796               continue;
4797             if ( faceNodeSet == initNodeSetNoCenter )
4798               continue;
4799             freeInd.push_back( iF );
4800             // find source edge of a free face iF
4801             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804                                    initNodeSet.begin(), initNodeSet.end(),
4805                                    commonNodes.begin());
4806             if ( (*v)->IsQuadratic() )
4807               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4808             else
4809               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4810 #ifdef _DEBUG_
4811             if ( !srcEdges.back() )
4812             {
4813               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814                    << iF << " of volume #" << vTool.ID() << endl;
4815             }
4816 #endif
4817           }
4818         }
4819         if ( freeInd.empty() )
4820           continue;
4821
4822         // create faces for all steps;
4823         // if such a face has been already created by sweep of edge,
4824         // assure that its orientation is OK
4825         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827           vTool.SetExternalNormal();
4828           const int nextShift = vTool.IsForward() ? +1 : -1;
4829           list< int >::iterator ind = freeInd.begin();
4830           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4832           {
4833             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834             int nbn = vTool.NbFaceNodes( *ind );
4835             const SMDS_MeshElement * f = 0;
4836             if ( nbn == 3 )              ///// triangle
4837             {
4838               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4839               if ( !f ||
4840                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4841               {
4842                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4843                                                      nodes[ 1 ],
4844                                                      nodes[ 1 + nextShift ] };
4845                 if ( f )
4846                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4847                 else
4848                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4849                                                             newOrder[ 2 ] ));
4850               }
4851             }
4852             else if ( nbn == 4 )       ///// quadrangle
4853             {
4854               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4855               if ( !f ||
4856                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4857               {
4858                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4860                 if ( f )
4861                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4862                 else
4863                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864                                                             newOrder[ 2 ], newOrder[ 3 ]));
4865               }
4866             }
4867             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4868             {
4869               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4870               if ( !f ||
4871                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4872               {
4873                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4874                                                      nodes[2],
4875                                                      nodes[2 + 2*nextShift],
4876                                                      nodes[3 - 2*nextShift],
4877                                                      nodes[3],
4878                                                      nodes[3 + 2*nextShift]};
4879                 if ( f )
4880                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4881                 else
4882                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4883                                                             newOrder[ 1 ],
4884                                                             newOrder[ 2 ],
4885                                                             newOrder[ 3 ],
4886                                                             newOrder[ 4 ],
4887                                                             newOrder[ 5 ] ));
4888               }
4889             }
4890             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4891             {
4892               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4894               if ( !f ||
4895                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4896               {
4897                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898                                                      nodes[4 - 2*nextShift],
4899                                                      nodes[4],
4900                                                      nodes[4 + 2*nextShift],
4901                                                      nodes[1],
4902                                                      nodes[5 - 2*nextShift],
4903                                                      nodes[5],
4904                                                      nodes[5 + 2*nextShift] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909                                                            newOrder[ 2 ], newOrder[ 3 ],
4910                                                            newOrder[ 4 ], newOrder[ 5 ],
4911                                                            newOrder[ 6 ], newOrder[ 7 ]));
4912               }
4913             }
4914             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4915             {
4916               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917                                       SMDSAbs_Face, /*noMedium=*/false);
4918               if ( !f ||
4919                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922                                                      nodes[4 - 2*nextShift],
4923                                                      nodes[4],
4924                                                      nodes[4 + 2*nextShift],
4925                                                      nodes[1],
4926                                                      nodes[5 - 2*nextShift],
4927                                                      nodes[5],
4928                                                      nodes[5 + 2*nextShift],
4929                                                      nodes[8] };
4930                 if ( f )
4931                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932                 else
4933                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934                                                            newOrder[ 2 ], newOrder[ 3 ],
4935                                                            newOrder[ 4 ], newOrder[ 5 ],
4936                                                            newOrder[ 6 ], newOrder[ 7 ],
4937                                                            newOrder[ 8 ]));
4938               }
4939             }
4940             else  //////// polygon
4941             {
4942               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4944               if ( !f ||
4945                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4946               {
4947                 if ( !vTool.IsForward() )
4948                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4951                 else
4952                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4953               }
4954             }
4955
4956             while ( srcElements.Length() < myLastCreatedElems.Length() )
4957               srcElements.Append( *srcEdge );
4958
4959           }  // loop on free faces
4960
4961           // go to the next volume
4962           iVol = 0;
4963           while ( iVol++ < nbVolumesByStep ) v++;
4964
4965         } // loop on steps
4966       } // loop on volumes of one step
4967     } // sweep free links into faces
4968
4969     // Make a ceiling face with a normal external to a volume
4970
4971     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4974
4975     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4978     }
4979     if ( iF >= 0 ) {
4980       lastVol.SetExternalNormal();
4981       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982       int nbn = lastVol.NbFaceNodes( iF );
4983       // we do not use this->AddElement() because nodes are interlaced
4984       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985       if ( !hasFreeLinks ||
4986            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4987       {
4988         if ( nbn == 3 )
4989           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4990
4991         else if ( nbn == 4 )
4992           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4993
4994         else if ( nbn == 6 && isQuadratic )
4995           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996                                                     nodes[1], nodes[3], nodes[5]));
4997         else if ( nbn == 7 && isQuadratic )
4998           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5000         else if ( nbn == 8 && isQuadratic )
5001           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5003         else if ( nbn == 9 && isQuadratic )
5004           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5006                                                     nodes[8]));
5007         else
5008           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5009
5010         while ( srcElements.Length() < myLastCreatedElems.Length() )
5011           srcElements.Append( elem );
5012       }
5013     }
5014   } // loop on swept elements
5015 }
5016
5017 //=======================================================================
5018 //function : RotationSweep
5019 //purpose  :
5020 //=======================================================================
5021
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024                                 const gp_Ax1&      theAxis,
5025                                 const double       theAngle,
5026                                 const int          theNbSteps,
5027                                 const double       theTol,
5028                                 const bool         theMakeGroups,
5029                                 const bool         theMakeWalls)
5030 {
5031   myLastCreatedElems.Clear();
5032   myLastCreatedNodes.Clear();
5033
5034   // source elements for each generated one
5035   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5036
5037   MESSAGE( "RotationSweep()");
5038   gp_Trsf aTrsf;
5039   aTrsf.SetRotation( theAxis, theAngle );
5040   gp_Trsf aTrsf2;
5041   aTrsf2.SetRotation( theAxis, theAngle/2. );
5042
5043   gp_Lin aLine( theAxis );
5044   double aSqTol = theTol * theTol;
5045
5046   SMESHDS_Mesh* aMesh = GetMeshDS();
5047
5048   TNodeOfNodeListMap mapNewNodes;
5049   TElemOfVecOfNnlmiMap mapElemNewNodes;
5050   TTElemOfElemListMap newElemsMap;
5051
5052   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5054                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5055   // loop on theElems
5056   TIDSortedElemSet::iterator itElem;
5057   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058     const SMDS_MeshElement* elem = *itElem;
5059     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5060       continue;
5061     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062     newNodesItVec.reserve( elem->NbNodes() );
5063
5064     // loop on elem nodes
5065     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066     while ( itN->more() )
5067     {
5068       // check if a node has been already sweeped
5069       const SMDS_MeshNode* node = cast2Node( itN->next() );
5070
5071       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5072       double coord[3];
5073       aXYZ.Coord( coord[0], coord[1], coord[2] );
5074       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5075
5076       TNodeOfNodeListMapItr nIt =
5077         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079       if ( listNewNodes.empty() )
5080       {
5081         // check if we are to create medium nodes between corner ones
5082         bool needMediumNodes = false;
5083         if ( isQuadraticMesh )
5084         {
5085           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086           while (it->more() && !needMediumNodes )
5087           {
5088             const SMDS_MeshElement* invElem = it->next();
5089             if ( invElem != elem && !theElems.count( invElem )) continue;
5090             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092               needMediumNodes = true;
5093           }
5094         }
5095
5096         // make new nodes
5097         const SMDS_MeshNode * newNode = node;
5098         for ( int i = 0; i < theNbSteps; i++ ) {
5099           if ( !isOnAxis ) {
5100             if ( needMediumNodes )  // create a medium node
5101             {
5102               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104               myLastCreatedNodes.Append(newNode);
5105               srcNodes.Append( node );
5106               listNewNodes.push_back( newNode );
5107               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5108             }
5109             else {
5110               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5111             }
5112             // create a corner node
5113             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114             myLastCreatedNodes.Append(newNode);
5115             srcNodes.Append( node );
5116             listNewNodes.push_back( newNode );
5117           }
5118           else {
5119             listNewNodes.push_back( newNode );
5120             // if ( needMediumNodes )
5121             //   listNewNodes.push_back( newNode );
5122           }
5123         }
5124       }
5125       newNodesItVec.push_back( nIt );
5126     }
5127     // make new elements
5128     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5129   }
5130
5131   if ( theMakeWalls )
5132     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5133
5134   PGroupIDs newGroupIDs;
5135   if ( theMakeGroups )
5136     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5137
5138   return newGroupIDs;
5139 }
5140
5141
5142 //=======================================================================
5143 //function : CreateNode
5144 //purpose  :
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5147                                                   const double y,
5148                                                   const double z,
5149                                                   const double tolnode,
5150                                                   SMESH_SequenceOfNode& aNodes)
5151 {
5152   // myLastCreatedElems.Clear();
5153   // myLastCreatedNodes.Clear();
5154
5155   gp_Pnt P1(x,y,z);
5156   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5157
5158   // try to search in sequence of existing nodes
5159   // if aNodes.Length()>0 we 'nave to use given sequence
5160   // else - use all nodes of mesh
5161   if(aNodes.Length()>0) {
5162     int i;
5163     for(i=1; i<=aNodes.Length(); i++) {
5164       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165       if(P1.Distance(P2)<tolnode)
5166         return aNodes.Value(i);
5167     }
5168   }
5169   else {
5170     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171     while(itn->more()) {
5172       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174       if(P1.Distance(P2)<tolnode)
5175         return aN;
5176     }
5177   }
5178
5179   // create new node and return it
5180   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181   //myLastCreatedNodes.Append(NewNode);
5182   return NewNode;
5183 }
5184
5185
5186 //=======================================================================
5187 //function : ExtrusionSweep
5188 //purpose  :
5189 //=======================================================================
5190
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5193                                   const gp_Vec&        theStep,
5194                                   const int            theNbSteps,
5195                                   TTElemOfElemListMap& newElemsMap,
5196                                   const bool           theMakeGroups,
5197                                   const int            theFlags,
5198                                   const double         theTolerance)
5199 {
5200   ExtrusParam aParams;
5201   aParams.myDir = gp_Dir(theStep);
5202   aParams.myNodes.Clear();
5203   aParams.mySteps = new TColStd_HSequenceOfReal;
5204   int i;
5205   for(i=1; i<=theNbSteps; i++)
5206     aParams.mySteps->Append(theStep.Magnitude());
5207
5208   return
5209     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5210 }
5211
5212
5213 //=======================================================================
5214 //function : ExtrusionSweep
5215 //purpose  :
5216 //=======================================================================
5217
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5220                                   ExtrusParam&         theParams,
5221                                   TTElemOfElemListMap& newElemsMap,
5222                                   const bool           theMakeGroups,
5223                                   const int            theFlags,
5224                                   const double         theTolerance)
5225 {
5226   myLastCreatedElems.Clear();
5227   myLastCreatedNodes.Clear();
5228
5229   // source elements for each generated one
5230   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5231
5232   SMESHDS_Mesh* aMesh = GetMeshDS();
5233
5234   int nbsteps = theParams.mySteps->Length();
5235
5236   TNodeOfNodeListMap mapNewNodes;
5237   //TNodeOfNodeVecMap mapNewNodes;
5238   TElemOfVecOfNnlmiMap mapElemNewNodes;
5239   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5240
5241   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5243                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5244   // loop on theElems
5245   TIDSortedElemSet::iterator itElem;
5246   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247     // check element type
5248     const SMDS_MeshElement* elem = *itElem;
5249     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5250       continue;
5251
5252     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253     newNodesItVec.reserve( elem->NbNodes() );
5254
5255     // loop on elem nodes
5256     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257     while ( itN->more() )
5258     {
5259       // check if a node has been already sweeped
5260       const SMDS_MeshNode* node = cast2Node( itN->next() );
5261       TNodeOfNodeListMap::iterator nIt =
5262         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264       if ( listNewNodes.empty() )
5265       {
5266         // make new nodes
5267
5268         // check if we are to create medium nodes between corner ones
5269         bool needMediumNodes = false;
5270         if ( isQuadraticMesh )
5271         {
5272           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273           while (it->more() && !needMediumNodes )
5274           {
5275             const SMDS_MeshElement* invElem = it->next();
5276             if ( invElem != elem && !theElems.count( invElem )) continue;
5277             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279               needMediumNodes = true;
5280           }
5281         }
5282
5283         double coord[] = { node->X(), node->Y(), node->Z() };
5284         for ( int i = 0; i < nbsteps; i++ )
5285         {
5286           if ( needMediumNodes ) // create a medium node
5287           {
5288             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291             if( theFlags & EXTRUSION_FLAG_SEW ) {
5292               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293                                                          theTolerance, theParams.myNodes);
5294               listNewNodes.push_back( newNode );
5295             }
5296             else {
5297               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298               myLastCreatedNodes.Append(newNode);
5299               srcNodes.Append( node );
5300               listNewNodes.push_back( newNode );
5301             }
5302           }
5303           // create a corner node
5304           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307           if( theFlags & EXTRUSION_FLAG_SEW ) {
5308             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309                                                        theTolerance, theParams.myNodes);
5310             listNewNodes.push_back( newNode );
5311           }
5312           else {
5313             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314             myLastCreatedNodes.Append(newNode);
5315             srcNodes.Append( node );
5316             listNewNodes.push_back( newNode );
5317           }
5318         }
5319       }
5320       newNodesItVec.push_back( nIt );
5321     }
5322     // make new elements
5323     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5324   }
5325
5326   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5328   }
5329   PGroupIDs newGroupIDs;
5330   if ( theMakeGroups )
5331     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5332
5333   return newGroupIDs;
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5338 //purpose  :
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5342                                        SMESH_subMesh*       theTrack,
5343                                        const SMDS_MeshNode* theN1,
5344                                        const bool           theHasAngles,
5345                                        list<double>&        theAngles,
5346                                        const bool           theLinearVariation,
5347                                        const bool           theHasRefPoint,
5348                                        const gp_Pnt&        theRefPoint,
5349                                        const bool           theMakeGroups)
5350 {
5351   MESSAGE("ExtrusionAlongTrack");
5352   myLastCreatedElems.Clear();
5353   myLastCreatedNodes.Clear();
5354
5355   int aNbE;
5356   std::list<double> aPrms;
5357   TIDSortedElemSet::iterator itElem;
5358
5359   gp_XYZ aGC;
5360   TopoDS_Edge aTrackEdge;
5361   TopoDS_Vertex aV1, aV2;
5362
5363   SMDS_ElemIteratorPtr aItE;
5364   SMDS_NodeIteratorPtr aItN;
5365   SMDSAbs_ElementType aTypeE;
5366
5367   TNodeOfNodeListMap mapNewNodes;
5368
5369   // 1. Check data
5370   aNbE = theElements.size();
5371   // nothing to do
5372   if ( !aNbE )
5373     return EXTR_NO_ELEMENTS;
5374
5375   // 1.1 Track Pattern
5376   ASSERT( theTrack );
5377
5378   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5379
5380   aItE = pSubMeshDS->GetElements();
5381   while ( aItE->more() ) {
5382     const SMDS_MeshElement* pE = aItE->next();
5383     aTypeE = pE->GetType();
5384     // Pattern must contain links only
5385     if ( aTypeE != SMDSAbs_Edge )
5386       return EXTR_PATH_NOT_EDGE;
5387   }
5388
5389   list<SMESH_MeshEditor_PathPoint> fullList;
5390
5391   const TopoDS_Shape& aS = theTrack->GetSubShape();
5392   // Sub-shape for the Pattern must be an Edge or Wire
5393   if( aS.ShapeType() == TopAbs_EDGE ) {
5394     aTrackEdge = TopoDS::Edge( aS );
5395     // the Edge must not be degenerated
5396     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397       return EXTR_BAD_PATH_SHAPE;
5398     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400     const SMDS_MeshNode* aN1 = aItN->next();
5401     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402     const SMDS_MeshNode* aN2 = aItN->next();
5403     // starting node must be aN1 or aN2
5404     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405       return EXTR_BAD_STARTING_NODE;
5406     aItN = pSubMeshDS->GetNodes();
5407     while ( aItN->more() ) {
5408       const SMDS_MeshNode* pNode = aItN->next();
5409       const SMDS_EdgePosition* pEPos =
5410         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411       double aT = pEPos->GetUParameter();
5412       aPrms.push_back( aT );
5413     }
5414     //Extrusion_Error err =
5415     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417     list< SMESH_subMesh* > LSM;
5418     TopTools_SequenceOfShape Edges;
5419     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420     while(itSM->more()) {
5421       SMESH_subMesh* SM = itSM->next();
5422       LSM.push_back(SM);
5423       const TopoDS_Shape& aS = SM->GetSubShape();
5424       Edges.Append(aS);
5425     }
5426     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427     int startNid = theN1->GetID();
5428     TColStd_MapOfInteger UsedNums;
5429
5430     int NbEdges = Edges.Length();
5431     int i = 1;
5432     for(; i<=NbEdges; i++) {
5433       int k = 0;
5434       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435       for(; itLSM!=LSM.end(); itLSM++) {
5436         k++;
5437         if(UsedNums.Contains(k)) continue;
5438         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439         SMESH_subMesh* locTrack = *itLSM;
5440         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443         const SMDS_MeshNode* aN1 = aItN->next();
5444         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445         const SMDS_MeshNode* aN2 = aItN->next();
5446         // starting node must be aN1 or aN2
5447         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448         // 2. Collect parameters on the track edge
5449         aPrms.clear();
5450         aItN = locMeshDS->GetNodes();
5451         while ( aItN->more() ) {
5452           const SMDS_MeshNode* pNode = aItN->next();
5453           const SMDS_EdgePosition* pEPos =
5454             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455           double aT = pEPos->GetUParameter();
5456           aPrms.push_back( aT );
5457         }
5458         list<SMESH_MeshEditor_PathPoint> LPP;
5459         //Extrusion_Error err =
5460         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461         LLPPs.push_back(LPP);
5462         UsedNums.Add(k);
5463         // update startN for search following egde
5464         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465         else startNid = aN1->GetID();
5466         break;
5467       }
5468     }
5469     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472     for(; itPP!=firstList.end(); itPP++) {
5473       fullList.push_back( *itPP );
5474     }
5475     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476     fullList.pop_back();
5477     itLLPP++;
5478     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480       itPP = currList.begin();
5481       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482       gp_Dir D1 = PP1.Tangent();
5483       gp_Dir D2 = PP2.Tangent();
5484       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485                            (D1.Z()+D2.Z())/2 ) );
5486       PP1.SetTangent(Dnew);
5487       fullList.push_back(PP1);
5488       itPP++;
5489       for(; itPP!=firstList.end(); itPP++) {
5490         fullList.push_back( *itPP );
5491       }
5492       PP1 = fullList.back();
5493       fullList.pop_back();
5494     }
5495     // if wire not closed
5496     fullList.push_back(PP1);
5497     // else ???
5498   }
5499   else {
5500     return EXTR_BAD_PATH_SHAPE;
5501   }
5502
5503   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504                           theHasRefPoint, theRefPoint, theMakeGroups);
5505 }
5506
5507
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5510 //purpose  :
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5514                                        SMESH_Mesh*          theTrack,
5515                                        const SMDS_MeshNode* theN1,
5516                                        const bool           theHasAngles,
5517                                        list<double>&        theAngles,
5518                                        const bool           theLinearVariation,
5519                                        const bool           theHasRefPoint,
5520                                        const gp_Pnt&        theRefPoint,
5521                                        const bool           theMakeGroups)
5522 {
5523   myLastCreatedElems.Clear();
5524   myLastCreatedNodes.Clear();
5525
5526   int aNbE;
5527   std::list<double> aPrms;
5528   TIDSortedElemSet::iterator itElem;
5529
5530   gp_XYZ aGC;
5531   TopoDS_Edge aTrackEdge;
5532   TopoDS_Vertex aV1, aV2;
5533
5534   SMDS_ElemIteratorPtr aItE;
5535   SMDS_NodeIteratorPtr aItN;
5536   SMDSAbs_ElementType aTypeE;
5537
5538   TNodeOfNodeListMap mapNewNodes;
5539
5540   // 1. Check data
5541   aNbE = theElements.size();
5542   // nothing to do
5543   if ( !aNbE )
5544     return EXTR_NO_ELEMENTS;
5545
5546   // 1.1 Track Pattern
5547   ASSERT( theTrack );
5548
5549   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5550
5551   aItE = pMeshDS->elementsIterator();
5552   while ( aItE->more() ) {
5553     const SMDS_MeshElement* pE = aItE->next();
5554     aTypeE = pE->GetType();
5555     // Pattern must contain links only
5556     if ( aTypeE != SMDSAbs_Edge )
5557       return EXTR_PATH_NOT_EDGE;
5558   }
5559
5560   list<SMESH_MeshEditor_PathPoint> fullList;
5561
5562   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5563
5564   if ( !theTrack->HasShapeToMesh() ) {
5565     //Mesh without shape
5566     const SMDS_MeshNode* currentNode = NULL;
5567     const SMDS_MeshNode* prevNode = theN1;
5568     std::vector<const SMDS_MeshNode*> aNodesList;
5569     aNodesList.push_back(theN1);
5570     int nbEdges = 0, conn=0;
5571     const SMDS_MeshElement* prevElem = NULL;
5572     const SMDS_MeshElement* currentElem = NULL;
5573     int totalNbEdges = theTrack->NbEdges();
5574     SMDS_ElemIteratorPtr nIt;
5575
5576     //check start node
5577     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578       return EXTR_BAD_STARTING_NODE;
5579     }
5580
5581     conn = nbEdgeConnectivity(theN1);
5582     if(conn > 2)
5583       return EXTR_PATH_NOT_EDGE;
5584
5585     aItE = theN1->GetInverseElementIterator();
5586     prevElem = aItE->next();
5587     currentElem = prevElem;
5588     //Get all nodes
5589     if(totalNbEdges == 1 ) {
5590       nIt = currentElem->nodesIterator();
5591       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592       if(currentNode == prevNode)
5593         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594       aNodesList.push_back(currentNode);
5595     } else {
5596       nIt = currentElem->nodesIterator();
5597       while( nIt->more() ) {
5598         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599         if(currentNode == prevNode)
5600           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601         aNodesList.push_back(currentNode);
5602
5603         //case of the closed mesh
5604         if(currentNode == theN1) {
5605           nbEdges++;
5606           break;
5607         }
5608
5609         conn = nbEdgeConnectivity(currentNode);
5610         if(conn > 2) {
5611           return EXTR_PATH_NOT_EDGE;
5612         }else if( conn == 1 && nbEdges > 0 ) {
5613           //End of the path
5614           nbEdges++;
5615           break;
5616         }else {
5617           prevNode = currentNode;
5618           aItE = currentNode->GetInverseElementIterator();
5619           currentElem = aItE->next();
5620           if( currentElem  == prevElem)
5621             currentElem = aItE->next();
5622           nIt = currentElem->nodesIterator();
5623           prevElem = currentElem;
5624           nbEdges++;
5625         }
5626       }
5627     }
5628
5629     if(nbEdges != totalNbEdges)
5630       return EXTR_PATH_NOT_EDGE;
5631
5632     TopTools_SequenceOfShape Edges;
5633     double x1,x2,y1,y2,z1,z2;
5634     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635     int startNid = theN1->GetID();
5636     for(int i = 1; i < aNodesList.size(); i++) {
5637       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641       list<SMESH_MeshEditor_PathPoint> LPP;
5642       aPrms.clear();
5643       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644       LLPPs.push_back(LPP);
5645       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646       else startNid = aNodesList[i-1]->GetID();
5647
5648     }
5649
5650     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653     for(; itPP!=firstList.end(); itPP++) {
5654       fullList.push_back( *itPP );
5655     }
5656
5657     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658     SMESH_MeshEditor_PathPoint PP2;
5659     fullList.pop_back();
5660     itLLPP++;
5661     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663       itPP = currList.begin();
5664       PP2 = currList.front();
5665       gp_Dir D1 = PP1.Tangent();
5666       gp_Dir D2 = PP2.Tangent();
5667       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668                            (D1.Z()+D2.Z())/2 ) );
5669       PP1.SetTangent(Dnew);
5670       fullList.push_back(PP1);
5671       itPP++;
5672       for(; itPP!=currList.end(); itPP++) {
5673         fullList.push_back( *itPP );
5674       }
5675       PP1 = fullList.back();
5676       fullList.pop_back();
5677     }
5678     fullList.push_back(PP1);
5679
5680   } // Sub-shape for the Pattern must be an Edge or Wire
5681   else if( aS.ShapeType() == TopAbs_EDGE ) {
5682     aTrackEdge = TopoDS::Edge( aS );
5683     // the Edge must not be degenerated
5684     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685       return EXTR_BAD_PATH_SHAPE;
5686     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689     // starting node must be aN1 or aN2
5690     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691       return EXTR_BAD_STARTING_NODE;
5692     aItN = pMeshDS->nodesIterator();
5693     while ( aItN->more() ) {
5694       const SMDS_MeshNode* pNode = aItN->next();
5695       if( pNode==aN1 || pNode==aN2 ) continue;
5696       const SMDS_EdgePosition* pEPos =
5697         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698       double aT = pEPos->GetUParameter();
5699       aPrms.push_back( aT );
5700     }
5701     //Extrusion_Error err =
5702     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5703   }
5704   else if( aS.ShapeType() == TopAbs_WIRE ) {
5705     list< SMESH_subMesh* > LSM;
5706     TopTools_SequenceOfShape Edges;
5707     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708     for(; eExp.More(); eExp.Next()) {
5709       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710       if( SMESH_Algo::isDegenerated(E) ) continue;
5711       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5712       if(SM) {
5713         LSM.push_back(SM);
5714         Edges.Append(E);
5715       }
5716     }
5717     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718     TopoDS_Vertex aVprev;
5719     TColStd_MapOfInteger UsedNums;
5720     int NbEdges = Edges.Length();
5721     int i = 1;
5722     for(; i<=NbEdges; i++) {
5723       int k = 0;
5724       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725       for(; itLSM!=LSM.end(); itLSM++) {
5726         k++;
5727         if(UsedNums.Contains(k)) continue;
5728         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729         SMESH_subMesh* locTrack = *itLSM;
5730         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732         bool aN1isOK = false, aN2isOK = false;
5733         if ( aVprev.IsNull() ) {
5734           // if previous vertex is not yet defined, it means that we in the beginning of wire
5735           // and we have to find initial vertex corresponding to starting node theN1
5736           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738           // starting node must be aN1 or aN2
5739           aN1isOK = ( aN1 && aN1 == theN1 );
5740           aN2isOK = ( aN2 && aN2 == theN1 );
5741         }
5742         else {
5743           // we have specified ending vertex of the previous edge on the previous iteration
5744           // and we have just to check that it corresponds to any vertex in current segment
5745           aN1isOK = aVprev.IsSame( aV1 );
5746           aN2isOK = aVprev.IsSame( aV2 );
5747         }
5748         if ( !aN1isOK && !aN2isOK ) continue;
5749         // 2. Collect parameters on the track edge
5750         aPrms.clear();
5751         aItN = locMeshDS->GetNodes();
5752         while ( aItN->more() ) {
5753           const SMDS_MeshNode*     pNode = aItN->next();
5754           const SMDS_EdgePosition* pEPos =
5755             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756           double aT = pEPos->GetUParameter();
5757           aPrms.push_back( aT );
5758         }
5759         list<SMESH_MeshEditor_PathPoint> LPP;
5760         //Extrusion_Error err =
5761         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762         LLPPs.push_back(LPP);
5763         UsedNums.Add(k);
5764         // update startN for search following egde
5765         if ( aN1isOK ) aVprev = aV2;
5766         else           aVprev = aV1;
5767         break;
5768       }
5769     }
5770     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772     fullList.splice( fullList.end(), firstList );
5773
5774     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775     fullList.pop_back();
5776     itLLPP++;
5777     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780       gp_Dir D1 = PP1.Tangent();
5781       gp_Dir D2 = PP2.Tangent();
5782       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783       PP1.SetTangent(Dnew);
5784       fullList.push_back(PP1);
5785       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786       PP1 = fullList.back();
5787       fullList.pop_back();
5788     }
5789     // if wire not closed
5790     fullList.push_back(PP1);
5791     // else ???
5792   }
5793   else {
5794     return EXTR_BAD_PATH_SHAPE;
5795   }
5796
5797   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798                           theHasRefPoint, theRefPoint, theMakeGroups);
5799 }
5800
5801
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose  : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5808                                      const TopoDS_Edge&                aTrackEdge,
5809                                      bool                              FirstIsStart,
5810                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5811 {
5812   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5813   aTolVec=1.e-7;
5814   aTolVec2=aTolVec*aTolVec;
5815   double aT1, aT2;
5816   TopoDS_Vertex aV1, aV2;
5817   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820   // 2. Collect parameters on the track edge
5821   aPrms.push_front( aT1 );
5822   aPrms.push_back( aT2 );
5823   // sort parameters
5824   aPrms.sort();
5825   if( FirstIsStart ) {
5826     if ( aT1 > aT2 ) {
5827       aPrms.reverse();
5828     }
5829   }
5830   else {
5831     if ( aT2 > aT1 ) {
5832       aPrms.reverse();
5833     }
5834   }
5835   // 3. Path Points
5836   SMESH_MeshEditor_PathPoint aPP;
5837   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838   std::list<double>::iterator aItD = aPrms.begin();
5839   for(; aItD != aPrms.end(); ++aItD) {
5840     double aT = *aItD;
5841     gp_Pnt aP3D;
5842     gp_Vec aVec;
5843     aC3D->D1( aT, aP3D, aVec );
5844     aL2 = aVec.SquareMagnitude();
5845     if ( aL2 < aTolVec2 )
5846       return EXTR_CANT_GET_TANGENT;
5847     gp_Dir aTgt( aVec );
5848     aPP.SetPnt( aP3D );
5849     aPP.SetTangent( aTgt );
5850     aPP.SetParameter( aT );
5851     LPP.push_back(aPP);
5852   }
5853   return EXTR_OK;
5854 }
5855
5856
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose  : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5863                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5864                                    const bool                        theHasAngles,
5865                                    list<double>&                     theAngles,
5866                                    const bool                        theLinearVariation,
5867                                    const bool                        theHasRefPoint,
5868                                    const gp_Pnt&                     theRefPoint,
5869                                    const bool                        theMakeGroups)
5870 {
5871   const int aNbTP = fullList.size();
5872   // Angles
5873   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874     LinearAngleVariation(aNbTP-1, theAngles);
5875   // fill vector of path points with angles
5876   vector<SMESH_MeshEditor_PathPoint> aPPs;
5877   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878   list<double>::iterator                 itAngles = theAngles.begin();
5879   aPPs.push_back( *itPP++ );
5880   for( ; itPP != fullList.end(); itPP++) {
5881     aPPs.push_back( *itPP );
5882     if ( theHasAngles && itAngles != theAngles.end() )
5883       aPPs.back().SetAngle( *itAngles++ );
5884   }
5885
5886   TNodeOfNodeListMap   mapNewNodes;
5887   TElemOfVecOfNnlmiMap mapElemNewNodes;
5888   TTElemOfElemListMap  newElemsMap;
5889   TIDSortedElemSet::iterator itElem;
5890   // source elements for each generated one
5891   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892
5893   // 3. Center of rotation aV0
5894   gp_Pnt aV0 = theRefPoint;
5895   if ( !theHasRefPoint )
5896   {
5897     gp_XYZ aGC( 0.,0.,0. );
5898     TIDSortedElemSet newNodes;
5899
5900     itElem = theElements.begin();
5901     for ( ; itElem != theElements.end(); itElem++ ) {
5902       const SMDS_MeshElement* elem = *itElem;
5903
5904       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905       while ( itN->more() ) {
5906         const SMDS_MeshElement* node = itN->next();
5907         if ( newNodes.insert( node ).second )
5908           aGC += SMESH_TNodeXYZ( node );
5909       }
5910     }
5911     aGC /= newNodes.size();
5912     aV0.SetXYZ( aGC );
5913   } // if (!theHasRefPoint) {
5914
5915   // 4. Processing the elements
5916   SMESHDS_Mesh* aMesh = GetMeshDS();
5917
5918   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919     // check element type
5920     const SMDS_MeshElement* elem = *itElem;
5921     SMDSAbs_ElementType   aTypeE = elem->GetType();
5922     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5923       continue;
5924
5925     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926     newNodesItVec.reserve( elem->NbNodes() );
5927
5928     // loop on elem nodes
5929     int nodeIndex = -1;
5930     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931     while ( itN->more() )
5932     {
5933       ++nodeIndex;
5934       // check if a node has been already processed
5935       const SMDS_MeshNode* node =
5936         static_cast<const SMDS_MeshNode*>( itN->next() );
5937       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938       if ( nIt == mapNewNodes.end() ) {
5939         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5941
5942         // make new nodes
5943         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945         gp_Ax1 anAx1, anAxT1T0;
5946         gp_Dir aDT1x, aDT0x, aDT1T0;
5947
5948         aTolAng=1.e-4;
5949
5950         aV0x = aV0;
5951         aPN0 = SMESH_TNodeXYZ( node );
5952
5953         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5954         aP0x = aPP0.Pnt();
5955         aDT0x= aPP0.Tangent();
5956         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5957
5958         for ( int j = 1; j < aNbTP; ++j ) {
5959           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5960           aP1x     = aPP1.Pnt();
5961           aDT1x    = aPP1.Tangent();
5962           aAngle1x = aPP1.Angle();
5963
5964           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5965           // Translation
5966           gp_Vec aV01x( aP0x, aP1x );
5967           aTrsf.SetTranslation( aV01x );
5968
5969           // traslated point
5970           aV1x = aV0x.Transformed( aTrsf );
5971           aPN1 = aPN0.Transformed( aTrsf );
5972
5973           // rotation 1 [ T1,T0 ]
5974           aAngleT1T0=-aDT1x.Angle( aDT0x );
5975           if (fabs(aAngleT1T0) > aTolAng) {
5976             aDT1T0=aDT1x^aDT0x;
5977             anAxT1T0.SetLocation( aV1x );
5978             anAxT1T0.SetDirection( aDT1T0 );
5979             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5980
5981             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5982           }
5983
5984           // rotation 2
5985           if ( theHasAngles ) {
5986             anAx1.SetLocation( aV1x );
5987             anAx1.SetDirection( aDT1x );
5988             aTrsfRot.SetRotation( anAx1, aAngle1x );
5989
5990             aPN1 = aPN1.Transformed( aTrsfRot );
5991           }
5992
5993           // make new node
5994           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996             // create additional node
5997             double x = ( aPN1.X() + aPN0.X() )/2.;
5998             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001             myLastCreatedNodes.Append(newNode);
6002             srcNodes.Append( node );
6003             listNewNodes.push_back( newNode );
6004           }
6005           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006           myLastCreatedNodes.Append(newNode);
6007           srcNodes.Append( node );
6008           listNewNodes.push_back( newNode );
6009
6010           aPN0 = aPN1;
6011           aP0x = aP1x;
6012           aV0x = aV1x;
6013           aDT0x = aDT1x;
6014         }
6015       }
6016
6017       else {
6018         // if current elem is quadratic and current node is not medium
6019         // we have to check - may be it is needed to insert additional nodes
6020         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022           if(listNewNodes.size()==aNbTP-1) {
6023             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024             gp_XYZ P(node->X(), node->Y(), node->Z());
6025             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6026             int i;
6027             for(i=0; i<aNbTP-1; i++) {
6028               const SMDS_MeshNode* N = *it;
6029               double x = ( N->X() + P.X() )/2.;
6030               double y = ( N->Y() + P.Y() )/2.;
6031               double z = ( N->Z() + P.Z() )/2.;
6032               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033               srcNodes.Append( node );
6034               myLastCreatedNodes.Append(newN);
6035               aNodes[2*i] = newN;
6036               aNodes[2*i+1] = N;
6037               P = gp_XYZ(N->X(),N->Y(),N->Z());
6038             }
6039             listNewNodes.clear();
6040             for(i=0; i<2*(aNbTP-1); i++) {
6041               listNewNodes.push_back(aNodes[i]);
6042             }
6043           }
6044         }
6045       }
6046
6047       newNodesItVec.push_back( nIt );
6048     }
6049     // make new elements
6050     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6052     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6053   }
6054
6055   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6056
6057   if ( theMakeGroups )
6058     generateGroups( srcNodes, srcElems, "extruded");
6059
6060   return EXTR_OK;
6061 }
6062
6063
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose  : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069                                             list<double>& Angles)
6070 {
6071   int nbAngles = Angles.size();
6072   if( nbSteps > nbAngles ) {
6073     vector<double> theAngles(nbAngles);
6074     list<double>::iterator it = Angles.begin();
6075     int i = -1;
6076     for(; it!=Angles.end(); it++) {
6077       i++;
6078       theAngles[i] = (*it);
6079     }
6080     list<double> res;
6081     double rAn2St = double( nbAngles ) / double( nbSteps );
6082     double angPrev = 0, angle;
6083     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084       double angCur = rAn2St * ( iSt+1 );
6085       double angCurFloor  = floor( angCur );
6086       double angPrevFloor = floor( angPrev );
6087       if ( angPrevFloor == angCurFloor )
6088         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6089       else {
6090         int iP = int( angPrevFloor );
6091         double angPrevCeil = ceil(angPrev);
6092         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6093
6094         int iC = int( angCurFloor );
6095         if ( iC < nbAngles )
6096           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6097
6098         iP = int( angPrevCeil );
6099         while ( iC-- > iP )
6100           angle += theAngles[ iC ];
6101       }
6102       res.push_back(angle);
6103       angPrev = angCur;
6104     }
6105     Angles.clear();
6106     it = res.begin();
6107     for(; it!=res.end(); it++)
6108       Angles.push_back( *it );
6109   }
6110 }
6111
6112
6113 //================================================================================
6114 /*!
6115  * \brief Move or copy theElements applying theTrsf to their nodes
6116  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117  *  \param theTrsf - transformation to apply
6118  *  \param theCopy - if true, create translated copies of theElems
6119  *  \param theMakeGroups - if true and theCopy, create translated groups
6120  *  \param theTargetMesh - mesh to copy translated elements into
6121  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6122  */
6123 //================================================================================
6124
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127                              const gp_Trsf&     theTrsf,
6128                              const bool         theCopy,
6129                              const bool         theMakeGroups,
6130                              SMESH_Mesh*        theTargetMesh)
6131 {
6132   myLastCreatedElems.Clear();
6133   myLastCreatedNodes.Clear();
6134
6135   bool needReverse = false;
6136   string groupPostfix;
6137   switch ( theTrsf.Form() ) {
6138   case gp_PntMirror:
6139     MESSAGE("gp_PntMirror");
6140     needReverse = true;
6141     groupPostfix = "mirrored";
6142     break;
6143   case gp_Ax1Mirror:
6144     MESSAGE("gp_Ax1Mirror");
6145     groupPostfix = "mirrored";
6146     break;
6147   case gp_Ax2Mirror:
6148     MESSAGE("gp_Ax2Mirror");
6149     needReverse = true;
6150     groupPostfix = "mirrored";
6151     break;
6152   case gp_Rotation:
6153     MESSAGE("gp_Rotation");
6154     groupPostfix = "rotated";
6155     break;
6156   case gp_Translation:
6157     MESSAGE("gp_Translation");
6158     groupPostfix = "translated";
6159     break;
6160   case gp_Scale:
6161     MESSAGE("gp_Scale");
6162     groupPostfix = "scaled";
6163     break;
6164   case gp_CompoundTrsf: // different scale by axis
6165     MESSAGE("gp_CompoundTrsf");
6166     groupPostfix = "scaled";
6167     break;
6168   default:
6169     MESSAGE("default");
6170     needReverse = false;
6171     groupPostfix = "transformed";
6172   }
6173
6174   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176   SMESHDS_Mesh* aMesh    = GetMeshDS();
6177
6178
6179   // map old node to new one
6180   TNodeNodeMap nodeMap;
6181
6182   // elements sharing moved nodes; those of them which have all
6183   // nodes mirrored but are not in theElems are to be reversed
6184   TIDSortedElemSet inverseElemSet;
6185
6186   // source elements for each generated one
6187   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6188
6189   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190   TIDSortedElemSet orphanNode;
6191
6192   if ( theElems.empty() ) // transform the whole mesh
6193   {
6194     // add all elements
6195     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196     while ( eIt->more() ) theElems.insert( eIt->next() );
6197     // add orphan nodes
6198     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199     while ( nIt->more() )
6200     {
6201       const SMDS_MeshNode* node = nIt->next();
6202       if ( node->NbInverseElements() == 0)
6203         orphanNode.insert( node );
6204     }
6205   }
6206
6207   // loop on elements to transform nodes : first orphan nodes then elems
6208   TIDSortedElemSet::iterator itElem;
6209   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210   for (int i=0; i<2; i++)
6211   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212     const SMDS_MeshElement* elem = *itElem;
6213     if ( !elem )
6214       continue;
6215
6216     // loop on elem nodes
6217     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218     while ( itN->more() ) {
6219
6220       const SMDS_MeshNode* node = cast2Node( itN->next() );
6221       // check if a node has been already transformed
6222       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223         nodeMap.insert( make_pair ( node, node ));
6224       if ( !n2n_isnew.second )
6225         continue;
6226
6227       double coord[3];
6228       coord[0] = node->X();
6229       coord[1] = node->Y();
6230       coord[2] = node->Z();
6231       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232       if ( theTargetMesh ) {
6233         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234         n2n_isnew.first->second = newNode;
6235         myLastCreatedNodes.Append(newNode);
6236         srcNodes.Append( node );
6237       }
6238       else if ( theCopy ) {
6239         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240         n2n_isnew.first->second = newNode;
6241         myLastCreatedNodes.Append(newNode);
6242         srcNodes.Append( node );
6243       }
6244       else {
6245         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246         // node position on shape becomes invalid
6247         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248           ( SMDS_SpacePosition::originSpacePosition() );
6249       }
6250
6251       // keep inverse elements
6252       if ( !theCopy && !theTargetMesh && needReverse ) {
6253         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254         while ( invElemIt->more() ) {
6255           const SMDS_MeshElement* iel = invElemIt->next();
6256           inverseElemSet.insert( iel );
6257         }
6258       }
6259     }
6260   }
6261
6262   // either create new elements or reverse mirrored ones
6263   if ( !theCopy && !needReverse && !theTargetMesh )
6264     return PGroupIDs();
6265
6266   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268     theElems.insert( *invElemIt );
6269
6270   // Replicate or reverse elements
6271
6272   std::vector<int> iForw;
6273   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6274   {
6275     const SMDS_MeshElement* elem = *itElem;
6276     if ( !elem ) continue;
6277
6278     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279     int                  nbNodes  = elem->NbNodes();
6280     if ( geomType == SMDSGeom_NONE ) continue; // node
6281
6282     switch ( geomType ) {
6283
6284     case SMDSGeom_POLYGON:  // ---------------------- polygon
6285       {
6286         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6287         int iNode = 0;
6288         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289         while (itN->more()) {
6290           const SMDS_MeshNode* node =
6291             static_cast<const SMDS_MeshNode*>(itN->next());
6292           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293           if (nodeMapIt == nodeMap.end())
6294             break; // not all nodes transformed
6295           if (needReverse) {
6296             // reverse mirrored faces and volumes
6297             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6298           } else {
6299             poly_nodes[iNode] = (*nodeMapIt).second;
6300           }
6301           iNode++;
6302         }
6303         if ( iNode != nbNodes )
6304           continue; // not all nodes transformed
6305
6306         if ( theTargetMesh ) {
6307           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308           srcElems.Append( elem );
6309         }
6310         else if ( theCopy ) {
6311           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312           srcElems.Append( elem );
6313         }
6314         else {
6315           aMesh->ChangePolygonNodes(elem, poly_nodes);
6316         }
6317       }
6318       break;
6319
6320     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6321       {
6322         const SMDS_VtkVolume* aPolyedre =
6323           dynamic_cast<const SMDS_VtkVolume*>( elem );
6324         if (!aPolyedre) {
6325           MESSAGE("Warning: bad volumic element");
6326           continue;
6327         }
6328
6329         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330         vector<int> quantities; quantities.reserve( nbNodes );
6331
6332         bool allTransformed = true;
6333         int nbFaces = aPolyedre->NbFaces();
6334         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339             if (nodeMapIt == nodeMap.end()) {
6340               allTransformed = false; // not all nodes transformed
6341             } else {
6342               poly_nodes.push_back((*nodeMapIt).second);
6343             }
6344             if ( needReverse && allTransformed )
6345               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6346           }
6347           quantities.push_back(nbFaceNodes);
6348         }
6349         if ( !allTransformed )
6350           continue; // not all nodes transformed
6351
6352         if ( theTargetMesh ) {
6353           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354           srcElems.Append( elem );
6355         }
6356         else if ( theCopy ) {
6357           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358           srcElems.Append( elem );
6359         }
6360         else {
6361           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6362         }
6363       }
6364       break;
6365
6366     case SMDSGeom_BALL: // -------------------- Ball
6367       {
6368         if ( !theCopy && !theTargetMesh ) continue;
6369
6370         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371         if (nodeMapIt == nodeMap.end())
6372           continue; // not all nodes transformed
6373
6374         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375         if ( theTargetMesh ) {
6376           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377           srcElems.Append( elem );
6378         }
6379         else {
6380           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381           srcElems.Append( elem );
6382         }
6383       }
6384       break;
6385
6386     default: // ----------------------- Regular elements
6387
6388       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390       const std::vector<int>& i = needReverse ? iRev : iForw;
6391
6392       // find transformed nodes
6393       vector<const SMDS_MeshNode*> nodes(nbNodes);
6394       int iNode = 0;
6395       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396       while ( itN->more() ) {
6397         const SMDS_MeshNode* node =
6398           static_cast<const SMDS_MeshNode*>( itN->next() );
6399         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400         if ( nodeMapIt == nodeMap.end() )
6401           break; // not all nodes transformed
6402         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6403       }
6404       if ( iNode != nbNodes )
6405         continue; // not all nodes transformed
6406
6407       if ( theTargetMesh ) {
6408         if ( SMDS_MeshElement* copy =
6409              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410           myLastCreatedElems.Append( copy );
6411           srcElems.Append( elem );
6412         }
6413       }
6414       else if ( theCopy ) {
6415         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416           srcElems.Append( elem );
6417       }
6418       else {
6419         // reverse element as it was reversed by transformation
6420         if ( nbNodes > 2 )
6421           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6422       }
6423     } // switch ( geomType )
6424
6425   } // loop on elements
6426
6427   PGroupIDs newGroupIDs;
6428
6429   if ( ( theMakeGroups && theCopy ) ||
6430        ( theMakeGroups && theTargetMesh ) )
6431     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6432
6433   return newGroupIDs;
6434 }
6435
6436 //=======================================================================
6437 /*!
6438  * \brief Create groups of elements made during transformation
6439  * \param nodeGens - nodes making corresponding myLastCreatedNodes
6440  * \param elemGens - elements making corresponding myLastCreatedElems
6441  * \param postfix - to append to names of new groups
6442  */
6443 //=======================================================================
6444
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447                                  const SMESH_SequenceOfElemPtr& elemGens,
6448                                  const std::string&             postfix,
6449                                  SMESH_Mesh*                    targetMesh)
6450 {
6451   PGroupIDs newGroupIDs( new list<int> );
6452   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6453
6454   // Sort existing groups by types and collect their names
6455
6456   // to store an old group and a generated new ones
6457   using boost::tuple;
6458   using boost::make_tuple;
6459   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6460   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6461   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6462   // group names
6463   set< string > groupNames;
6464
6465   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6466   if ( !groupIt->more() ) return newGroupIDs;
6467
6468   int newGroupID = mesh->GetGroupIds().back()+1;
6469   while ( groupIt->more() )
6470   {
6471     SMESH_Group * group = groupIt->next();
6472     if ( !group ) continue;
6473     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6474     if ( !groupDS || groupDS->IsEmpty() ) continue;
6475     groupNames.insert    ( group->GetName() );
6476     groupDS->SetStoreName( group->GetName() );
6477     const SMDSAbs_ElementType type = groupDS->GetType();
6478     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6479     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6480     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6481     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6482   }
6483
6484   // Loop on nodes and elements to add them in new groups
6485
6486   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6487   {
6488     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6489     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6490     if ( gens.Length() != elems.Length() )
6491       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6492
6493     // loop on created elements
6494     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6495     {
6496       const SMDS_MeshElement* sourceElem = gens( iElem );
6497       if ( !sourceElem ) {
6498         MESSAGE("generateGroups(): NULL source element");
6499         continue;
6500       }
6501       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6502       if ( groupsOldNew.empty() ) { // no groups of this type at all
6503         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6504           ++iElem; // skip all elements made by sourceElem
6505         continue;
6506       }
6507       // collect all elements made by the iElem-th sourceElem
6508       list< const SMDS_MeshElement* > resultElems;
6509       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6510         if ( resElem != sourceElem )
6511           resultElems.push_back( resElem );
6512       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6513         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6514           if ( resElem != sourceElem )
6515             resultElems.push_back( resElem );
6516
6517       // there must be a top element
6518       const SMDS_MeshElement* topElem = 0;
6519       if ( isNodes )
6520       {
6521         topElem = resultElems.back();
6522         resultElems.pop_back();
6523       }
6524       else
6525       {
6526         list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6527         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6528           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6529           {
6530             topElem = *resElemIt;
6531             resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6532             break;
6533           }
6534       }
6535
6536       // add resultElems to groups originted from ones the sourceElem belongs to
6537       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6538       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6539       {
6540         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6541         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6542         {
6543           // fill in a new group
6544           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6545           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6546           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6547             newGroup.Add( *resElemIt );
6548
6549           // fill a "top" group
6550           if ( topElem )
6551           {
6552             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6553             newTopGroup.Add( topElem );
6554           }
6555         }
6556       }
6557     } // loop on created elements
6558   }// loop on nodes and elements
6559
6560   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6561
6562   list<int> topGrouIds;
6563   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6564   {
6565     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6566     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6567                                       orderedOldNewGroups[i]->get<2>() };
6568     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6569     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6570     {
6571       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6572       if ( newGroupDS->IsEmpty() )
6573       {
6574         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6575       }
6576       else
6577       {
6578         // set group type
6579         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6580
6581         // make a name
6582         const bool isTop = ( nbNewGroups == 2 &&
6583                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6584                              is2nd );
6585
6586         string name = oldGroupDS->GetStoreName();
6587         if ( !targetMesh ) {
6588           string suffix = ( isTop ? "top": postfix.c_str() );
6589           name += "_";
6590           name += suffix;
6591           int nb = 1;
6592           while ( !groupNames.insert( name ).second ) // name exists
6593             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6594         }
6595         else if ( isTop ) {
6596           name += "_top";
6597         }
6598         newGroupDS->SetStoreName( name.c_str() );
6599
6600         // make a SMESH_Groups
6601         mesh->AddGroup( newGroupDS );
6602         if ( isTop )
6603           topGrouIds.push_back( newGroupDS->GetID() );
6604         else
6605           newGroupIDs->push_back( newGroupDS->GetID() );
6606       }
6607     }
6608   }
6609   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6610
6611   return newGroupIDs;
6612 }
6613
6614 //================================================================================
6615 /*!
6616  * \brief Return list of group of nodes close to each other within theTolerance
6617  *        Search among theNodes or in the whole mesh if theNodes is empty using
6618  *        an Octree algorithm
6619  */
6620 //================================================================================
6621
6622 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6623                                             const double         theTolerance,
6624                                             TListOfListOfNodes & theGroupsOfNodes)
6625 {
6626   myLastCreatedElems.Clear();
6627   myLastCreatedNodes.Clear();
6628
6629   if ( theNodes.empty() )
6630   { // get all nodes in the mesh
6631     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6632     while ( nIt->more() )
6633       theNodes.insert( theNodes.end(),nIt->next());
6634   }
6635
6636   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6637 }
6638
6639 //=======================================================================
6640 //function : SimplifyFace
6641 //purpose  :
6642 //=======================================================================
6643
6644 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6645                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6646                                     vector<int>&                         quantities) const
6647 {
6648   int nbNodes = faceNodes.size();
6649
6650   if (nbNodes < 3)
6651     return 0;
6652
6653   set<const SMDS_MeshNode*> nodeSet;
6654
6655   // get simple seq of nodes
6656   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6657   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6658   int iSimple = 0, nbUnique = 0;
6659
6660   simpleNodes[iSimple++] = faceNodes[0];
6661   nbUnique++;
6662   for (int iCur = 1; iCur < nbNodes; iCur++) {
6663     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6664       simpleNodes[iSimple++] = faceNodes[iCur];
6665       if (nodeSet.insert( faceNodes[iCur] ).second)
6666         nbUnique++;
6667     }
6668   }
6669   int nbSimple = iSimple;
6670   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6671     nbSimple--;
6672     iSimple--;
6673   }
6674
6675   if (nbUnique < 3)
6676     return 0;
6677
6678   // separate loops
6679   int nbNew = 0;
6680   bool foundLoop = (nbSimple > nbUnique);
6681   while (foundLoop) {
6682     foundLoop = false;
6683     set<const SMDS_MeshNode*> loopSet;
6684     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6685       const SMDS_MeshNode* n = simpleNodes[iSimple];
6686       if (!loopSet.insert( n ).second) {
6687         foundLoop = true;
6688
6689         // separate loop
6690         int iC = 0, curLast = iSimple;
6691         for (; iC < curLast; iC++) {
6692           if (simpleNodes[iC] == n) break;
6693         }
6694         int loopLen = curLast - iC;
6695         if (loopLen > 2) {
6696           // create sub-element
6697           nbNew++;
6698           quantities.push_back(loopLen);
6699           for (; iC < curLast; iC++) {
6700             poly_nodes.push_back(simpleNodes[iC]);
6701           }
6702         }
6703         // shift the rest nodes (place from the first loop position)
6704         for (iC = curLast + 1; iC < nbSimple; iC++) {
6705           simpleNodes[iC - loopLen] = simpleNodes[iC];
6706         }
6707         nbSimple -= loopLen;
6708         iSimple -= loopLen;
6709       }
6710     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6711   } // while (foundLoop)
6712
6713   if (iSimple > 2) {
6714     nbNew++;
6715     quantities.push_back(iSimple);
6716     for (int i = 0; i < iSimple; i++)
6717       poly_nodes.push_back(simpleNodes[i]);
6718   }
6719
6720   return nbNew;
6721 }
6722
6723 //=======================================================================
6724 //function : MergeNodes
6725 //purpose  : In each group, the cdr of nodes are substituted by the first one
6726 //           in all elements.
6727 //=======================================================================
6728
6729 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6730 {
6731   MESSAGE("MergeNodes");
6732   myLastCreatedElems.Clear();
6733   myLastCreatedNodes.Clear();
6734
6735   SMESHDS_Mesh* aMesh = GetMeshDS();
6736
6737   TNodeNodeMap nodeNodeMap; // node to replace - new node
6738   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6739   list< int > rmElemIds, rmNodeIds;
6740
6741   // Fill nodeNodeMap and elems
6742
6743   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6744   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6745     list<const SMDS_MeshNode*>& nodes = *grIt;
6746     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6747     const SMDS_MeshNode* nToKeep = *nIt;
6748     //MESSAGE("node to keep " << nToKeep->GetID());
6749     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6750       const SMDS_MeshNode* nToRemove = *nIt;
6751       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6752       if ( nToRemove != nToKeep ) {
6753         //MESSAGE("  node to remove " << nToRemove->GetID());
6754         rmNodeIds.push_back( nToRemove->GetID() );
6755         AddToSameGroups( nToKeep, nToRemove, aMesh );
6756         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6757         // after MergeNodes() w/o creating node in place of merged ones.
6758         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6759         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6760           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6761             sm->SetIsAlwaysComputed( true );
6762       }
6763
6764       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6765       while ( invElemIt->more() ) {
6766         const SMDS_MeshElement* elem = invElemIt->next();
6767         elems.insert(elem);
6768       }
6769     }
6770   }
6771   // Change element nodes or remove an element
6772
6773   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6774   for ( ; eIt != elems.end(); eIt++ ) {
6775     const SMDS_MeshElement* elem = *eIt;
6776     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6777     int nbNodes = elem->NbNodes();
6778     int aShapeId = FindShape( elem );
6779
6780     set<const SMDS_MeshNode*> nodeSet;
6781     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6782     int iUnique = 0, iCur = 0, nbRepl = 0;
6783     vector<int> iRepl( nbNodes );
6784
6785     // get new seq of nodes
6786     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6787     while ( itN->more() ) {
6788       const SMDS_MeshNode* n =
6789         static_cast<const SMDS_MeshNode*>( itN->next() );
6790
6791       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6792       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6793         n = (*nnIt).second;
6794         // BUG 0020185: begin
6795         {
6796           bool stopRecur = false;
6797           set<const SMDS_MeshNode*> nodesRecur;
6798           nodesRecur.insert(n);
6799           while (!stopRecur) {
6800             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6801             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6802               n = (*nnIt_i).second;
6803               if (!nodesRecur.insert(n).second) {
6804                 // error: recursive dependancy
6805                 stopRecur = true;
6806               }
6807             }
6808             else
6809               stopRecur = true;
6810           }
6811         }
6812         // BUG 0020185: end
6813       }
6814       curNodes[ iCur ] = n;
6815       bool isUnique = nodeSet.insert( n ).second;
6816       if ( isUnique )
6817         uniqueNodes[ iUnique++ ] = n;
6818       else
6819         iRepl[ nbRepl++ ] = iCur;
6820       iCur++;
6821     }
6822
6823     // Analyse element topology after replacement
6824
6825     bool isOk = true;
6826     int nbUniqueNodes = nodeSet.size();
6827     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6828     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6829       // Polygons and Polyhedral volumes
6830       if (elem->IsPoly()) {
6831
6832         if (elem->GetType() == SMDSAbs_Face) {
6833           // Polygon
6834           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6835           int inode = 0;
6836           for (; inode < nbNodes; inode++) {
6837             face_nodes[inode] = curNodes[inode];
6838           }
6839
6840           vector<const SMDS_MeshNode *> polygons_nodes;
6841           vector<int> quantities;
6842           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6843           if (nbNew > 0) {
6844             inode = 0;
6845             for (int iface = 0; iface < nbNew; iface++) {
6846               int nbNodes = quantities[iface];
6847               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6848               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6849                 poly_nodes[ii] = polygons_nodes[inode];
6850               }
6851               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6852               myLastCreatedElems.Append(newElem);
6853               if (aShapeId)
6854                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6855             }
6856
6857             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6858             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6859             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6860             int quid =0;
6861             if (nbNew > 0) quid = nbNew - 1;
6862             vector<int> newquant(quantities.begin()+quid, quantities.end());
6863             const SMDS_MeshElement* newElem = 0;
6864             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6865             myLastCreatedElems.Append(newElem);
6866             if ( aShapeId && newElem )
6867               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6868             rmElemIds.push_back(elem->GetID());
6869           }
6870           else {
6871             rmElemIds.push_back(elem->GetID());
6872           }
6873
6874         }
6875         else if (elem->GetType() == SMDSAbs_Volume) {
6876           // Polyhedral volume
6877           if (nbUniqueNodes < 4) {
6878             rmElemIds.push_back(elem->GetID());
6879           }
6880           else {
6881             // each face has to be analyzed in order to check volume validity
6882             const SMDS_VtkVolume* aPolyedre =
6883               dynamic_cast<const SMDS_VtkVolume*>( elem );
6884             if (aPolyedre) {
6885               int nbFaces = aPolyedre->NbFaces();
6886
6887               vector<const SMDS_MeshNode *> poly_nodes;
6888               vector<int> quantities;
6889
6890               for (int iface = 1; iface <= nbFaces; iface++) {
6891                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6892                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6893
6894                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6895                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6896                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6897                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6898                     faceNode = (*nnIt).second;
6899                   }
6900                   faceNodes[inode - 1] = faceNode;
6901                 }
6902
6903                 SimplifyFace(faceNodes, poly_nodes, quantities);
6904               }
6905
6906               if (quantities.size() > 3) {
6907                 // to be done: remove coincident faces
6908               }
6909
6910               if (quantities.size() > 3)
6911                 {
6912                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6913                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6914                   const SMDS_MeshElement* newElem = 0;
6915                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6916                   myLastCreatedElems.Append(newElem);
6917                   if ( aShapeId && newElem )
6918                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6919                   rmElemIds.push_back(elem->GetID());
6920                 }
6921             }
6922             else {
6923               rmElemIds.push_back(elem->GetID());
6924             }
6925           }
6926         }
6927         else {
6928         }
6929
6930         continue;
6931       } // poly element
6932
6933       // Regular elements
6934       // TODO not all the possible cases are solved. Find something more generic?
6935       switch ( nbNodes ) {
6936       case 2: ///////////////////////////////////// EDGE
6937         isOk = false; break;
6938       case 3: ///////////////////////////////////// TRIANGLE
6939         isOk = false; break;
6940       case 4:
6941         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6942           isOk = false;
6943         else { //////////////////////////////////// QUADRANGLE
6944           if ( nbUniqueNodes < 3 )
6945             isOk = false;
6946           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6947             isOk = false; // opposite nodes stick
6948           //MESSAGE("isOk " << isOk);
6949         }
6950         break;
6951       case 6: ///////////////////////////////////// PENTAHEDRON
6952         if ( nbUniqueNodes == 4 ) {
6953           // ---------------------------------> tetrahedron
6954           if (nbRepl == 3 &&
6955               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6956             // all top nodes stick: reverse a bottom
6957             uniqueNodes[ 0 ] = curNodes [ 1 ];
6958             uniqueNodes[ 1 ] = curNodes [ 0 ];
6959           }
6960           else if (nbRepl == 3 &&
6961                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6962             // all bottom nodes stick: set a top before
6963             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6964             uniqueNodes[ 0 ] = curNodes [ 3 ];
6965             uniqueNodes[ 1 ] = curNodes [ 4 ];
6966             uniqueNodes[ 2 ] = curNodes [ 5 ];
6967           }
6968           else if (nbRepl == 4 &&
6969                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6970             // a lateral face turns into a line: reverse a bottom
6971             uniqueNodes[ 0 ] = curNodes [ 1 ];
6972             uniqueNodes[ 1 ] = curNodes [ 0 ];
6973           }
6974           else
6975             isOk = false;
6976         }
6977         else if ( nbUniqueNodes == 5 ) {
6978           // PENTAHEDRON --------------------> 2 tetrahedrons
6979           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6980             // a bottom node sticks with a linked top one
6981             // 1.
6982             SMDS_MeshElement* newElem =
6983               aMesh->AddVolume(curNodes[ 3 ],
6984                                curNodes[ 4 ],
6985                                curNodes[ 5 ],
6986                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6987             myLastCreatedElems.Append(newElem);
6988             if ( aShapeId )
6989               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6990             // 2. : reverse a bottom
6991             uniqueNodes[ 0 ] = curNodes [ 1 ];
6992             uniqueNodes[ 1 ] = curNodes [ 0 ];
6993             nbUniqueNodes = 4;
6994           }
6995           else
6996             isOk = false;
6997         }
6998         else
6999           isOk = false;
7000         break;
7001       case 8: {
7002         if(elem->IsQuadratic()) { // Quadratic quadrangle
7003           //   1    5    2
7004           //    +---+---+
7005           //    |       |
7006           //    |       |
7007           //   4+       +6
7008           //    |       |
7009           //    |       |
7010           //    +---+---+
7011           //   0    7    3
7012           isOk = false;
7013           if(nbRepl==2) {
7014             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7015           }
7016           if(nbRepl==3) {
7017             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7018             nbUniqueNodes = 6;
7019             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7020               uniqueNodes[0] = curNodes[0];
7021               uniqueNodes[1] = curNodes[2];
7022               uniqueNodes[2] = curNodes[3];
7023               uniqueNodes[3] = curNodes[5];
7024               uniqueNodes[4] = curNodes[6];
7025               uniqueNodes[5] = curNodes[7];
7026               isOk = true;
7027             }
7028             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7029               uniqueNodes[0] = curNodes[0];
7030               uniqueNodes[1] = curNodes[1];
7031               uniqueNodes[2] = curNodes[2];
7032               uniqueNodes[3] = curNodes[4];
7033               uniqueNodes[4] = curNodes[5];
7034               uniqueNodes[5] = curNodes[6];
7035               isOk = true;
7036             }
7037             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7038               uniqueNodes[0] = curNodes[1];
7039               uniqueNodes[1] = curNodes[2];
7040               uniqueNodes[2] = curNodes[3];
7041               uniqueNodes[3] = curNodes[5];
7042               uniqueNodes[4] = curNodes[6];
7043               uniqueNodes[5] = curNodes[0];
7044               isOk = true;
7045             }
7046             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7047               uniqueNodes[0] = curNodes[0];
7048               uniqueNodes[1] = curNodes[1];
7049               uniqueNodes[2] = curNodes[3];
7050               uniqueNodes[3] = curNodes[4];
7051               uniqueNodes[4] = curNodes[6];
7052               uniqueNodes[5] = curNodes[7];
7053               isOk = true;
7054             }
7055             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7056               uniqueNodes[0] = curNodes[0];
7057               uniqueNodes[1] = curNodes[2];
7058               uniqueNodes[2] = curNodes[3];
7059               uniqueNodes[3] = curNodes[1];
7060               uniqueNodes[4] = curNodes[6];
7061               uniqueNodes[5] = curNodes[7];
7062               isOk = true;
7063             }
7064             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7065               uniqueNodes[0] = curNodes[0];
7066               uniqueNodes[1] = curNodes[1];
7067               uniqueNodes[2] = curNodes[2];
7068               uniqueNodes[3] = curNodes[4];
7069               uniqueNodes[4] = curNodes[5];
7070               uniqueNodes[5] = curNodes[7];
7071               isOk = true;
7072             }
7073             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7074               uniqueNodes[0] = curNodes[0];
7075               uniqueNodes[1] = curNodes[1];
7076               uniqueNodes[2] = curNodes[3];
7077               uniqueNodes[3] = curNodes[4];
7078               uniqueNodes[4] = curNodes[2];
7079               uniqueNodes[5] = curNodes[7];
7080               isOk = true;
7081             }
7082             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7083               uniqueNodes[0] = curNodes[0];
7084               uniqueNodes[1] = curNodes[1];
7085               uniqueNodes[2] = curNodes[2];
7086               uniqueNodes[3] = curNodes[4];
7087               uniqueNodes[4] = curNodes[5];
7088               uniqueNodes[5] = curNodes[3];
7089               isOk = true;
7090             }
7091           }
7092           if(nbRepl==4) {
7093             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7094           }
7095           if(nbRepl==5) {
7096             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7097           }
7098           break;
7099         }
7100         //////////////////////////////////// HEXAHEDRON
7101         isOk = false;
7102         SMDS_VolumeTool hexa (elem);
7103         hexa.SetExternalNormal();
7104         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7105           //////////////////////// HEX ---> 1 tetrahedron
7106           for ( int iFace = 0; iFace < 6; iFace++ ) {
7107             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7108             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7109                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7110                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7111               // one face turns into a point ...
7112               int iOppFace = hexa.GetOppFaceIndex( iFace );
7113               ind = hexa.GetFaceNodesIndices( iOppFace );
7114               int nbStick = 0;
7115               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7116                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7117                   nbStick++;
7118               }
7119               if ( nbStick == 1 ) {
7120                 // ... and the opposite one - into a triangle.
7121                 // set a top node
7122                 ind = hexa.GetFaceNodesIndices( iFace );
7123                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7124                 isOk = true;
7125               }
7126               break;
7127             }
7128           }
7129         }
7130         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7131           //////////////////////// HEX ---> 1 prism
7132           int nbTria = 0, iTria[3];
7133           const int *ind; // indices of face nodes
7134           // look for triangular faces
7135           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7136             ind = hexa.GetFaceNodesIndices( iFace );
7137             TIDSortedNodeSet faceNodes;
7138             for ( iCur = 0; iCur < 4; iCur++ )
7139               faceNodes.insert( curNodes[ind[iCur]] );
7140             if ( faceNodes.size() == 3 )
7141               iTria[ nbTria++ ] = iFace;
7142           }
7143           // check if triangles are opposite
7144           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7145           {
7146             isOk = true;
7147             // set nodes of the bottom triangle
7148             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7149             vector<int> indB;
7150             for ( iCur = 0; iCur < 4; iCur++ )
7151               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7152                 indB.push_back( ind[iCur] );
7153             if ( !hexa.IsForward() )
7154               std::swap( indB[0], indB[2] );
7155             for ( iCur = 0; iCur < 3; iCur++ )
7156               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7157             // set nodes of the top triangle
7158             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7159             for ( iCur = 0; iCur < 3; ++iCur )
7160               for ( int j = 0; j < 4; ++j )
7161                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7162                 {
7163                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7164                   break;
7165                 }
7166           }
7167           break;
7168         }
7169         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7170           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7171           for ( int iFace = 0; iFace < 6; iFace++ ) {
7172             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7173             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7174                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7175                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7176               // one face turns into a point ...
7177               int iOppFace = hexa.GetOppFaceIndex( iFace );
7178               ind = hexa.GetFaceNodesIndices( iOppFace );
7179               int nbStick = 0;
7180               iUnique = 2;  // reverse a tetrahedron 1 bottom
7181               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7182                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7183                   nbStick++;
7184                 else if ( iUnique >= 0 )
7185                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7186               }
7187               if ( nbStick == 0 ) {
7188                 // ... and the opposite one is a quadrangle
7189                 // set a top node
7190                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7191                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7192                 nbUniqueNodes = 4;
7193                 // tetrahedron 2
7194                 SMDS_MeshElement* newElem =
7195                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7196                                    curNodes[ind[ 3 ]],
7197                                    curNodes[ind[ 2 ]],
7198                                    curNodes[indTop[ 0 ]]);
7199                 myLastCreatedElems.Append(newElem);
7200                 if ( aShapeId )
7201                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7202                 isOk = true;
7203               }
7204               break;
7205             }
7206           }
7207         }
7208         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7209           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7210           // find indices of quad and tri faces
7211           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7212           for ( iFace = 0; iFace < 6; iFace++ ) {
7213             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7214             nodeSet.clear();
7215             for ( iCur = 0; iCur < 4; iCur++ )
7216               nodeSet.insert( curNodes[ind[ iCur ]] );
7217             nbUniqueNodes = nodeSet.size();
7218             if ( nbUniqueNodes == 3 )
7219               iTriFace[ nbTri++ ] = iFace;
7220             else if ( nbUniqueNodes == 4 )
7221               iQuadFace[ nbQuad++ ] = iFace;
7222           }
7223           if (nbQuad == 2 && nbTri == 4 &&
7224               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7225             // 2 opposite quadrangles stuck with a diagonal;
7226             // sample groups of merged indices: (0-4)(2-6)
7227             // --------------------------------------------> 2 tetrahedrons
7228             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7229             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7230             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7231             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7232                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7233               // stuck with 0-2 diagonal
7234               i0  = ind1[ 3 ];
7235               i1d = ind1[ 0 ];
7236               i2  = ind1[ 1 ];
7237               i3d = ind1[ 2 ];
7238               i0t = ind2[ 1 ];
7239               i2t = ind2[ 3 ];
7240             }
7241             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7242                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7243               // stuck with 1-3 diagonal
7244               i0  = ind1[ 0 ];
7245               i1d = ind1[ 1 ];
7246               i2  = ind1[ 2 ];
7247               i3d = ind1[ 3 ];
7248               i0t = ind2[ 0 ];
7249               i2t = ind2[ 1 ];
7250             }
7251             else {
7252               ASSERT(0);
7253             }
7254             // tetrahedron 1
7255             uniqueNodes[ 0 ] = curNodes [ i0 ];
7256             uniqueNodes[ 1 ] = curNodes [ i1d ];
7257             uniqueNodes[ 2 ] = curNodes [ i3d ];
7258             uniqueNodes[ 3 ] = curNodes [ i0t ];
7259             nbUniqueNodes = 4;
7260             // tetrahedron 2
7261             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7262                                                          curNodes[ i2 ],
7263                                                          curNodes[ i3d ],
7264                                                          curNodes[ i2t ]);
7265             myLastCreatedElems.Append(newElem);
7266             if ( aShapeId )
7267               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7268             isOk = true;
7269           }
7270           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7271                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7272             // --------------------------------------------> prism
7273             // find 2 opposite triangles
7274             nbUniqueNodes = 6;
7275             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7276               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7277                 // find indices of kept and replaced nodes
7278                 // and fill unique nodes of 2 opposite triangles
7279                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7280                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7281                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7282                 // fill unique nodes
7283                 iUnique = 0;
7284                 isOk = true;
7285                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7286                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7287                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7288                   if ( n == nInit ) {
7289                     // iCur of a linked node of the opposite face (make normals co-directed):
7290                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7291                     // check that correspondent corners of triangles are linked
7292                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7293                       isOk = false;
7294                     else {
7295                       uniqueNodes[ iUnique ] = n;
7296                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7297                       iUnique++;
7298                     }
7299                   }
7300                 }
7301                 break;
7302               }
7303             }
7304           }
7305         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7306         else
7307         {
7308           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7309         }
7310         break;
7311       } // HEXAHEDRON
7312
7313       default:
7314         isOk = false;
7315       } // switch ( nbNodes )
7316
7317     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7318
7319     if ( isOk ) { // the elem remains valid after sticking nodes
7320       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7321       {
7322         // Change nodes of polyedre
7323         const SMDS_VtkVolume* aPolyedre =
7324           dynamic_cast<const SMDS_VtkVolume*>( elem );
7325         if (aPolyedre) {
7326           int nbFaces = aPolyedre->NbFaces();
7327
7328           vector<const SMDS_MeshNode *> poly_nodes;
7329           vector<int> quantities (nbFaces);
7330
7331           for (int iface = 1; iface <= nbFaces; iface++) {
7332             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7333             quantities[iface - 1] = nbFaceNodes;
7334
7335             for (inode = 1; inode <= nbFaceNodes; inode++) {
7336               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7337
7338               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7339               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7340                 curNode = (*nnIt).second;
7341               }
7342               poly_nodes.push_back(curNode);
7343             }
7344           }
7345           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7346         }
7347       }
7348       else // replace non-polyhedron elements
7349       {
7350         const SMDSAbs_ElementType etyp = elem->GetType();
7351         const int elemId               = elem->GetID();
7352         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7353         uniqueNodes.resize(nbUniqueNodes);
7354
7355         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7356
7357         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7358         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7359         if ( sm && newElem )
7360           sm->AddElement( newElem );
7361         if ( elem != newElem )
7362           ReplaceElemInGroups( elem, newElem, aMesh );
7363       }
7364     }
7365     else {
7366       // Remove invalid regular element or invalid polygon
7367       rmElemIds.push_back( elem->GetID() );
7368     }
7369
7370   } // loop on elements
7371
7372   // Remove bad elements, then equal nodes (order important)
7373
7374   Remove( rmElemIds, false );
7375   Remove( rmNodeIds, true );
7376
7377 }
7378
7379
7380 // ========================================================
7381 // class   : SortableElement
7382 // purpose : allow sorting elements basing on their nodes
7383 // ========================================================
7384 class SortableElement : public set <const SMDS_MeshElement*>
7385 {
7386 public:
7387
7388   SortableElement( const SMDS_MeshElement* theElem )
7389   {
7390     myElem = theElem;
7391     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7392     while ( nodeIt->more() )
7393       this->insert( nodeIt->next() );
7394   }
7395
7396   const SMDS_MeshElement* Get() const
7397   { return myElem; }
7398
7399   void Set(const SMDS_MeshElement* e) const
7400   { myElem = e; }
7401
7402
7403 private:
7404   mutable const SMDS_MeshElement* myElem;
7405 };
7406
7407 //=======================================================================
7408 //function : FindEqualElements
7409 //purpose  : Return list of group of elements built on the same nodes.
7410 //           Search among theElements or in the whole mesh if theElements is empty
7411 //=======================================================================
7412
7413 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7414                                          TListOfListOfElementsID & theGroupsOfElementsID)
7415 {
7416   myLastCreatedElems.Clear();
7417   myLastCreatedNodes.Clear();
7418
7419   typedef map< SortableElement, int > TMapOfNodeSet;
7420   typedef list<int> TGroupOfElems;
7421
7422   if ( theElements.empty() )
7423   { // get all elements in the mesh
7424     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7425     while ( eIt->more() )
7426       theElements.insert( theElements.end(), eIt->next());
7427   }
7428
7429   vector< TGroupOfElems > arrayOfGroups;
7430   TGroupOfElems groupOfElems;
7431   TMapOfNodeSet mapOfNodeSet;
7432
7433   TIDSortedElemSet::iterator elemIt = theElements.begin();
7434   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7435     const SMDS_MeshElement* curElem = *elemIt;
7436     SortableElement SE(curElem);
7437     int ind = -1;
7438     // check uniqueness
7439     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7440     if( !(pp.second) ) {
7441       TMapOfNodeSet::iterator& itSE = pp.first;
7442       ind = (*itSE).second;
7443       arrayOfGroups[ind].push_back(curElem->GetID());
7444     }
7445     else {
7446       groupOfElems.clear();
7447       groupOfElems.push_back(curElem->GetID());
7448       arrayOfGroups.push_back(groupOfElems);
7449       i++;
7450     }
7451   }
7452
7453   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7454   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7455     groupOfElems = *groupIt;
7456     if ( groupOfElems.size() > 1 ) {
7457       groupOfElems.sort();
7458       theGroupsOfElementsID.push_back(groupOfElems);
7459     }
7460   }
7461 }
7462
7463 //=======================================================================
7464 //function : MergeElements
7465 //purpose  : In each given group, substitute all elements by the first one.
7466 //=======================================================================
7467
7468 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7469 {
7470   myLastCreatedElems.Clear();
7471   myLastCreatedNodes.Clear();
7472
7473   typedef list<int> TListOfIDs;
7474   TListOfIDs rmElemIds; // IDs of elems to remove
7475
7476   SMESHDS_Mesh* aMesh = GetMeshDS();
7477
7478   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7479   while ( groupsIt != theGroupsOfElementsID.end() ) {
7480     TListOfIDs& aGroupOfElemID = *groupsIt;
7481     aGroupOfElemID.sort();
7482     int elemIDToKeep = aGroupOfElemID.front();
7483     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7484     aGroupOfElemID.pop_front();
7485     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7486     while ( idIt != aGroupOfElemID.end() ) {
7487       int elemIDToRemove = *idIt;
7488       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7489       // add the kept element in groups of removed one (PAL15188)
7490       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7491       rmElemIds.push_back( elemIDToRemove );
7492       ++idIt;
7493     }
7494     ++groupsIt;
7495   }
7496
7497   Remove( rmElemIds, false );
7498 }
7499
7500 //=======================================================================
7501 //function : MergeEqualElements
7502 //purpose  : Remove all but one of elements built on the same nodes.
7503 //=======================================================================
7504
7505 void SMESH_MeshEditor::MergeEqualElements()
7506 {
7507   TIDSortedElemSet aMeshElements; /* empty input ==
7508                                      to merge equal elements in the whole mesh */
7509   TListOfListOfElementsID aGroupsOfElementsID;
7510   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7511   MergeElements(aGroupsOfElementsID);
7512 }
7513
7514 //=======================================================================
7515 //function : findAdjacentFace
7516 //purpose  :
7517 //=======================================================================
7518
7519 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7520                                                 const SMDS_MeshNode* n2,
7521                                                 const SMDS_MeshElement* elem)
7522 {
7523   TIDSortedElemSet elemSet, avoidSet;
7524   if ( elem )
7525     avoidSet.insert ( elem );
7526   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7527 }
7528
7529 //=======================================================================
7530 //function : FindFreeBorder
7531 //purpose  :
7532 //=======================================================================
7533
7534 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7535
7536 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7537                                        const SMDS_MeshNode*             theSecondNode,
7538                                        const SMDS_MeshNode*             theLastNode,
7539                                        list< const SMDS_MeshNode* > &   theNodes,
7540                                        list< const SMDS_MeshElement* >& theFaces)
7541 {
7542   if ( !theFirstNode || !theSecondNode )
7543     return false;
7544   // find border face between theFirstNode and theSecondNode
7545   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7546   if ( !curElem )
7547     return false;
7548
7549   theFaces.push_back( curElem );
7550   theNodes.push_back( theFirstNode );
7551   theNodes.push_back( theSecondNode );
7552
7553   //vector<const SMDS_MeshNode*> nodes;
7554   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7555   TIDSortedElemSet foundElems;
7556   bool needTheLast = ( theLastNode != 0 );
7557
7558   while ( nStart != theLastNode ) {
7559     if ( nStart == theFirstNode )
7560       return !needTheLast;
7561
7562     // find all free border faces sharing form nStart
7563
7564     list< const SMDS_MeshElement* > curElemList;
7565     list< const SMDS_MeshNode* > nStartList;
7566     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7567     while ( invElemIt->more() ) {
7568       const SMDS_MeshElement* e = invElemIt->next();
7569       if ( e == curElem || foundElems.insert( e ).second ) {
7570         // get nodes
7571         int iNode = 0, nbNodes = e->NbNodes();
7572         //const SMDS_MeshNode* nodes[nbNodes+1];
7573         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7574
7575         if(e->IsQuadratic()) {
7576           const SMDS_VtkFace* F =
7577             dynamic_cast<const SMDS_VtkFace*>(e);
7578           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7579           // use special nodes iterator
7580           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7581           while( anIter->more() ) {
7582             nodes[ iNode++ ] = cast2Node(anIter->next());
7583           }
7584         }
7585         else {
7586           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7587           while ( nIt->more() )
7588             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7589         }
7590         nodes[ iNode ] = nodes[ 0 ];
7591         // check 2 links
7592         for ( iNode = 0; iNode < nbNodes; iNode++ )
7593           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7594                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7595               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7596           {
7597             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7598             curElemList.push_back( e );
7599           }
7600       }
7601     }
7602     // analyse the found
7603
7604     int nbNewBorders = curElemList.size();
7605     if ( nbNewBorders == 0 ) {
7606       // no free border furthermore
7607       return !needTheLast;
7608     }
7609     else if ( nbNewBorders == 1 ) {
7610       // one more element found
7611       nIgnore = nStart;
7612       nStart = nStartList.front();
7613       curElem = curElemList.front();
7614       theFaces.push_back( curElem );
7615       theNodes.push_back( nStart );
7616     }
7617     else {
7618       // several continuations found
7619       list< const SMDS_MeshElement* >::iterator curElemIt;
7620       list< const SMDS_MeshNode* >::iterator nStartIt;
7621       // check if one of them reached the last node
7622       if ( needTheLast ) {
7623         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7624              curElemIt!= curElemList.end();
7625              curElemIt++, nStartIt++ )
7626           if ( *nStartIt == theLastNode ) {
7627             theFaces.push_back( *curElemIt );
7628             theNodes.push_back( *nStartIt );
7629             return true;
7630           }
7631       }
7632       // find the best free border by the continuations
7633       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7634       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7635       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636            curElemIt!= curElemList.end();
7637            curElemIt++, nStartIt++ )
7638       {
7639         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7640         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7641         // find one more free border
7642         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7643           cNL->clear();
7644           cFL->clear();
7645         }
7646         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7647           // choice: clear a worse one
7648           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7649           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7650           contNodes[ iWorse ].clear();
7651           contFaces[ iWorse ].clear();
7652         }
7653       }
7654       if ( contNodes[0].empty() && contNodes[1].empty() )
7655         return false;
7656
7657       // append the best free border
7658       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7659       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7660       theNodes.pop_back(); // remove nIgnore
7661       theNodes.pop_back(); // remove nStart
7662       theFaces.pop_back(); // remove curElem
7663       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7664       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7665       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7666       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7667       return true;
7668
7669     } // several continuations found
7670   } // while ( nStart != theLastNode )
7671
7672   return true;
7673 }
7674
7675 //=======================================================================
7676 //function : CheckFreeBorderNodes
7677 //purpose  : Return true if the tree nodes are on a free border
7678 //=======================================================================
7679
7680 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7681                                             const SMDS_MeshNode* theNode2,
7682                                             const SMDS_MeshNode* theNode3)
7683 {
7684   list< const SMDS_MeshNode* > nodes;
7685   list< const SMDS_MeshElement* > faces;
7686   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7687 }
7688
7689 //=======================================================================
7690 //function : SewFreeBorder
7691 //purpose  :
7692 //=======================================================================
7693
7694 SMESH_MeshEditor::Sew_Error
7695 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7696                                  const SMDS_MeshNode* theBordSecondNode,
7697                                  const SMDS_MeshNode* theBordLastNode,
7698                                  const SMDS_MeshNode* theSideFirstNode,
7699                                  const SMDS_MeshNode* theSideSecondNode,
7700                                  const SMDS_MeshNode* theSideThirdNode,
7701                                  const bool           theSideIsFreeBorder,
7702                                  const bool           toCreatePolygons,
7703                                  const bool           toCreatePolyedrs)
7704 {
7705   myLastCreatedElems.Clear();
7706   myLastCreatedNodes.Clear();
7707
7708   MESSAGE("::SewFreeBorder()");
7709   Sew_Error aResult = SEW_OK;
7710
7711   // ====================================
7712   //    find side nodes and elements
7713   // ====================================
7714
7715   list< const SMDS_MeshNode* > nSide[ 2 ];
7716   list< const SMDS_MeshElement* > eSide[ 2 ];
7717   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7718   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7719
7720   // Free border 1
7721   // --------------
7722   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7723                       nSide[0], eSide[0])) {
7724     MESSAGE(" Free Border 1 not found " );
7725     aResult = SEW_BORDER1_NOT_FOUND;
7726   }
7727   if (theSideIsFreeBorder) {
7728     // Free border 2
7729     // --------------
7730     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7731                         nSide[1], eSide[1])) {
7732       MESSAGE(" Free Border 2 not found " );
7733       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7734     }
7735   }
7736   if ( aResult != SEW_OK )
7737     return aResult;
7738
7739   if (!theSideIsFreeBorder) {
7740     // Side 2
7741     // --------------
7742
7743     // -------------------------------------------------------------------------
7744     // Algo:
7745     // 1. If nodes to merge are not coincident, move nodes of the free border
7746     //    from the coord sys defined by the direction from the first to last
7747     //    nodes of the border to the correspondent sys of the side 2
7748     // 2. On the side 2, find the links most co-directed with the correspondent
7749     //    links of the free border
7750     // -------------------------------------------------------------------------
7751
7752     // 1. Since sewing may break if there are volumes to split on the side 2,
7753     //    we wont move nodes but just compute new coordinates for them
7754     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7755     TNodeXYZMap nBordXYZ;
7756     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7757     list< const SMDS_MeshNode* >::iterator nBordIt;
7758
7759     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7760     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7761     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7762     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7763     double tol2 = 1.e-8;
7764     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7765     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7766       // Need node movement.
7767
7768       // find X and Z axes to create trsf
7769       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7770       gp_Vec X = Zs ^ Zb;
7771       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7772         // Zb || Zs
7773         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7774
7775       // coord systems
7776       gp_Ax3 toBordAx( Pb1, Zb, X );
7777       gp_Ax3 fromSideAx( Ps1, Zs, X );
7778       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7779       // set trsf
7780       gp_Trsf toBordSys, fromSide2Sys;
7781       toBordSys.SetTransformation( toBordAx );
7782       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7783       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7784
7785       // move
7786       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7787         const SMDS_MeshNode* n = *nBordIt;
7788         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7789         toBordSys.Transforms( xyz );
7790         fromSide2Sys.Transforms( xyz );
7791         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7792       }
7793     }
7794     else {
7795       // just insert nodes XYZ in the nBordXYZ map
7796       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7797         const SMDS_MeshNode* n = *nBordIt;
7798         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7799       }
7800     }
7801
7802     // 2. On the side 2, find the links most co-directed with the correspondent
7803     //    links of the free border
7804
7805     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7806     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7807     sideNodes.push_back( theSideFirstNode );
7808
7809     bool hasVolumes = false;
7810     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7811     set<long> foundSideLinkIDs, checkedLinkIDs;
7812     SMDS_VolumeTool volume;
7813     //const SMDS_MeshNode* faceNodes[ 4 ];
7814
7815     const SMDS_MeshNode*    sideNode;
7816     const SMDS_MeshElement* sideElem;
7817     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7818     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7819     nBordIt = bordNodes.begin();
7820     nBordIt++;
7821     // border node position and border link direction to compare with
7822     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7823     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7824     // choose next side node by link direction or by closeness to
7825     // the current border node:
7826     bool searchByDir = ( *nBordIt != theBordLastNode );
7827     do {
7828       // find the next node on the Side 2
7829       sideNode = 0;
7830       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7831       long linkID;
7832       checkedLinkIDs.clear();
7833       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7834
7835       // loop on inverse elements of current node (prevSideNode) on the Side 2
7836       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7837       while ( invElemIt->more() )
7838       {
7839         const SMDS_MeshElement* elem = invElemIt->next();
7840         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7841         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7842         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7843         bool isVolume = volume.Set( elem );
7844         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7845         if ( isVolume ) // --volume
7846           hasVolumes = true;
7847         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7848           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7849           if(elem->IsQuadratic()) {
7850             const SMDS_VtkFace* F =
7851               dynamic_cast<const SMDS_VtkFace*>(elem);
7852             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7853             // use special nodes iterator
7854             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7855             while( anIter->more() ) {
7856               nodes[ iNode ] = cast2Node(anIter->next());
7857               if ( nodes[ iNode++ ] == prevSideNode )
7858                 iPrevNode = iNode - 1;
7859             }
7860           }
7861           else {
7862             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7863             while ( nIt->more() ) {
7864               nodes[ iNode ] = cast2Node( nIt->next() );
7865               if ( nodes[ iNode++ ] == prevSideNode )
7866                 iPrevNode = iNode - 1;
7867             }
7868           }
7869           // there are 2 links to check
7870           nbNodes = 2;
7871         }
7872         else // --edge
7873           continue;
7874         // loop on links, to be precise, on the second node of links
7875         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7876           const SMDS_MeshNode* n = nodes[ iNode ];
7877           if ( isVolume ) {
7878             if ( !volume.IsLinked( n, prevSideNode ))
7879               continue;
7880           }
7881           else {
7882             if ( iNode ) // a node before prevSideNode
7883               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7884             else         // a node after prevSideNode
7885               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7886           }
7887           // check if this link was already used
7888           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7889           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7890           if (!isJustChecked &&
7891               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7892           {
7893             // test a link geometrically
7894             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7895             bool linkIsBetter = false;
7896             double dot = 0.0, dist = 0.0;
7897             if ( searchByDir ) { // choose most co-directed link
7898               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7899               linkIsBetter = ( dot > maxDot );
7900             }
7901             else { // choose link with the node closest to bordPos
7902               dist = ( nextXYZ - bordPos ).SquareModulus();
7903               linkIsBetter = ( dist < minDist );
7904             }
7905             if ( linkIsBetter ) {
7906               maxDot = dot;
7907               minDist = dist;
7908               linkID = iLink;
7909               sideNode = n;
7910               sideElem = elem;
7911             }
7912           }
7913         }
7914       } // loop on inverse elements of prevSideNode
7915
7916       if ( !sideNode ) {
7917         MESSAGE(" Cant find path by links of the Side 2 ");
7918         return SEW_BAD_SIDE_NODES;
7919       }
7920       sideNodes.push_back( sideNode );
7921       sideElems.push_back( sideElem );
7922       foundSideLinkIDs.insert ( linkID );
7923       prevSideNode = sideNode;
7924
7925       if ( *nBordIt == theBordLastNode )
7926         searchByDir = false;
7927       else {
7928         // find the next border link to compare with
7929         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7930         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7931         // move to next border node if sideNode is before forward border node (bordPos)
7932         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7933           prevBordNode = *nBordIt;
7934           nBordIt++;
7935           bordPos = nBordXYZ[ *nBordIt ];
7936           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7937           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7938         }
7939       }
7940     }
7941     while ( sideNode != theSideSecondNode );
7942
7943     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7944       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7945       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7946     }
7947   } // end nodes search on the side 2
7948
7949   // ============================
7950   // sew the border to the side 2
7951   // ============================
7952
7953   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7954   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7955
7956   TListOfListOfNodes nodeGroupsToMerge;
7957   if ( nbNodes[0] == nbNodes[1] ||
7958        ( theSideIsFreeBorder && !theSideThirdNode)) {
7959
7960     // all nodes are to be merged
7961
7962     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7963          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7964          nIt[0]++, nIt[1]++ )
7965     {
7966       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7967       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7968       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7969     }
7970   }
7971   else {
7972
7973     // insert new nodes into the border and the side to get equal nb of segments
7974
7975     // get normalized parameters of nodes on the borders
7976     //double param[ 2 ][ maxNbNodes ];
7977     double* param[ 2 ];
7978     param[0] = new double [ maxNbNodes ];
7979     param[1] = new double [ maxNbNodes ];
7980     int iNode, iBord;
7981     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7982       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7983       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7984       const SMDS_MeshNode* nPrev = *nIt;
7985       double bordLength = 0;
7986       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7987         const SMDS_MeshNode* nCur = *nIt;
7988         gp_XYZ segment (nCur->X() - nPrev->X(),
7989                         nCur->Y() - nPrev->Y(),
7990                         nCur->Z() - nPrev->Z());
7991         double segmentLen = segment.Modulus();
7992         bordLength += segmentLen;
7993         param[ iBord ][ iNode ] = bordLength;
7994         nPrev = nCur;
7995       }
7996       // normalize within [0,1]
7997       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7998         param[ iBord ][ iNode ] /= bordLength;
7999       }
8000     }
8001
8002     // loop on border segments
8003     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8004     int i[ 2 ] = { 0, 0 };
8005     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8006     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8007
8008     TElemOfNodeListMap insertMap;
8009     TElemOfNodeListMap::iterator insertMapIt;
8010     // insertMap is
8011     // key:   elem to insert nodes into
8012     // value: 2 nodes to insert between + nodes to be inserted
8013     do {
8014       bool next[ 2 ] = { false, false };
8015
8016       // find min adjacent segment length after sewing
8017       double nextParam = 10., prevParam = 0;
8018       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8019         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8020           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8021         if ( i[ iBord ] > 0 )
8022           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8023       }
8024       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8025       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8026       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8027
8028       // choose to insert or to merge nodes
8029       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8030       if ( Abs( du ) <= minSegLen * 0.2 ) {
8031         // merge
8032         // ------
8033         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8034         const SMDS_MeshNode* n0 = *nIt[0];
8035         const SMDS_MeshNode* n1 = *nIt[1];
8036         nodeGroupsToMerge.back().push_back( n1 );
8037         nodeGroupsToMerge.back().push_back( n0 );
8038         // position of node of the border changes due to merge
8039         param[ 0 ][ i[0] ] += du;
8040         // move n1 for the sake of elem shape evaluation during insertion.
8041         // n1 will be removed by MergeNodes() anyway
8042         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8043         next[0] = next[1] = true;
8044       }
8045       else {
8046         // insert
8047         // ------
8048         int intoBord = ( du < 0 ) ? 0 : 1;
8049         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8050         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8051         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8052         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8053         if ( intoBord == 1 ) {
8054           // move node of the border to be on a link of elem of the side
8055           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8056           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8057           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8058           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8059           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8060         }
8061         insertMapIt = insertMap.find( elem );
8062         bool notFound = ( insertMapIt == insertMap.end() );
8063         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8064         if ( otherLink ) {
8065           // insert into another link of the same element:
8066           // 1. perform insertion into the other link of the elem
8067           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8068           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8069           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8070           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8071           // 2. perform insertion into the link of adjacent faces
8072           while (true) {
8073             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8074             if ( adjElem )
8075               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8076             else
8077               break;
8078           }
8079           if (toCreatePolyedrs) {
8080             // perform insertion into the links of adjacent volumes
8081             UpdateVolumes(n12, n22, nodeList);
8082           }
8083           // 3. find an element appeared on n1 and n2 after the insertion
8084           insertMap.erase( elem );
8085           elem = findAdjacentFace( n1, n2, 0 );
8086         }
8087         if ( notFound || otherLink ) {
8088           // add element and nodes of the side into the insertMap
8089           insertMapIt = insertMap.insert
8090             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8091           (*insertMapIt).second.push_back( n1 );
8092           (*insertMapIt).second.push_back( n2 );
8093         }
8094         // add node to be inserted into elem
8095         (*insertMapIt).second.push_back( nIns );
8096         next[ 1 - intoBord ] = true;
8097       }
8098
8099       // go to the next segment
8100       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8101         if ( next[ iBord ] ) {
8102           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8103             eIt[ iBord ]++;
8104           nPrev[ iBord ] = *nIt[ iBord ];
8105           nIt[ iBord ]++; i[ iBord ]++;
8106         }
8107       }
8108     }
8109     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8110
8111     // perform insertion of nodes into elements
8112
8113     for (insertMapIt = insertMap.begin();
8114          insertMapIt != insertMap.end();
8115          insertMapIt++ )
8116     {
8117       const SMDS_MeshElement* elem = (*insertMapIt).first;
8118       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8119       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8120       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8121
8122       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8123
8124       if ( !theSideIsFreeBorder ) {
8125         // look for and insert nodes into the faces adjacent to elem
8126         while (true) {
8127           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8128           if ( adjElem )
8129             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8130           else
8131             break;
8132         }
8133       }
8134       if (toCreatePolyedrs) {
8135         // perform insertion into the links of adjacent volumes
8136         UpdateVolumes(n1, n2, nodeList);
8137       }
8138     }
8139
8140     delete param[0];
8141     delete param[1];
8142   } // end: insert new nodes
8143
8144   MergeNodes ( nodeGroupsToMerge );
8145
8146   return aResult;
8147 }
8148
8149 //=======================================================================
8150 //function : InsertNodesIntoLink
8151 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8152 //           and theBetweenNode2 and split theElement
8153 //=======================================================================
8154
8155 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8156                                            const SMDS_MeshNode*        theBetweenNode1,
8157                                            const SMDS_MeshNode*        theBetweenNode2,
8158                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8159                                            const bool                  toCreatePoly)
8160 {
8161   if ( theFace->GetType() != SMDSAbs_Face ) return;
8162
8163   // find indices of 2 link nodes and of the rest nodes
8164   int iNode = 0, il1, il2, i3, i4;
8165   il1 = il2 = i3 = i4 = -1;
8166   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8167   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8168
8169   if(theFace->IsQuadratic()) {
8170     const SMDS_VtkFace* F =
8171       dynamic_cast<const SMDS_VtkFace*>(theFace);
8172     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8173     // use special nodes iterator
8174     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8175     while( anIter->more() ) {
8176       const SMDS_MeshNode* n = cast2Node(anIter->next());
8177       if ( n == theBetweenNode1 )
8178         il1 = iNode;
8179       else if ( n == theBetweenNode2 )
8180         il2 = iNode;
8181       else if ( i3 < 0 )
8182         i3 = iNode;
8183       else
8184         i4 = iNode;
8185       nodes[ iNode++ ] = n;
8186     }
8187   }
8188   else {
8189     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8190     while ( nodeIt->more() ) {
8191       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8192       if ( n == theBetweenNode1 )
8193         il1 = iNode;
8194       else if ( n == theBetweenNode2 )
8195         il2 = iNode;
8196       else if ( i3 < 0 )
8197         i3 = iNode;
8198       else
8199         i4 = iNode;
8200       nodes[ iNode++ ] = n;
8201     }
8202   }
8203   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8204     return ;
8205
8206   // arrange link nodes to go one after another regarding the face orientation
8207   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8208   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8209   if ( reverse ) {
8210     iNode = il1;
8211     il1 = il2;
8212     il2 = iNode;
8213     aNodesToInsert.reverse();
8214   }
8215   // check that not link nodes of a quadrangles are in good order
8216   int nbFaceNodes = theFace->NbNodes();
8217   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8218     iNode = i3;
8219     i3 = i4;
8220     i4 = iNode;
8221   }
8222
8223   if (toCreatePoly || theFace->IsPoly()) {
8224
8225     iNode = 0;
8226     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8227
8228     // add nodes of face up to first node of link
8229     bool isFLN = false;
8230
8231     if(theFace->IsQuadratic()) {
8232       const SMDS_VtkFace* F =
8233         dynamic_cast<const SMDS_VtkFace*>(theFace);
8234       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8235       // use special nodes iterator
8236       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8237       while( anIter->more()  && !isFLN ) {
8238         const SMDS_MeshNode* n = cast2Node(anIter->next());
8239         poly_nodes[iNode++] = n;
8240         if (n == nodes[il1]) {
8241           isFLN = true;
8242         }
8243       }
8244       // add nodes to insert
8245       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8246       for (; nIt != aNodesToInsert.end(); nIt++) {
8247         poly_nodes[iNode++] = *nIt;
8248       }
8249       // add nodes of face starting from last node of link
8250       while ( anIter->more() ) {
8251         poly_nodes[iNode++] = cast2Node(anIter->next());
8252       }
8253     }
8254     else {
8255       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8256       while ( nodeIt->more() && !isFLN ) {
8257         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8258         poly_nodes[iNode++] = n;
8259         if (n == nodes[il1]) {
8260           isFLN = true;
8261         }
8262       }
8263       // add nodes to insert
8264       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8265       for (; nIt != aNodesToInsert.end(); nIt++) {
8266         poly_nodes[iNode++] = *nIt;
8267       }
8268       // add nodes of face starting from last node of link
8269       while ( nodeIt->more() ) {
8270         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8271         poly_nodes[iNode++] = n;
8272       }
8273     }
8274
8275     // edit or replace the face
8276     SMESHDS_Mesh *aMesh = GetMeshDS();
8277
8278     if (theFace->IsPoly()) {
8279       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8280     }
8281     else {
8282       int aShapeId = FindShape( theFace );
8283
8284       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8285       myLastCreatedElems.Append(newElem);
8286       if ( aShapeId && newElem )
8287         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8288
8289       aMesh->RemoveElement(theFace);
8290     }
8291     return;
8292   }
8293
8294   SMESHDS_Mesh *aMesh = GetMeshDS();
8295   if( !theFace->IsQuadratic() ) {
8296
8297     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8298     int nbLinkNodes = 2 + aNodesToInsert.size();
8299     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8300     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8301     linkNodes[ 0 ] = nodes[ il1 ];
8302     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8303     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8304     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8305       linkNodes[ iNode++ ] = *nIt;
8306     }
8307     // decide how to split a quadrangle: compare possible variants
8308     // and choose which of splits to be a quadrangle
8309     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8310     if ( nbFaceNodes == 3 ) {
8311       iBestQuad = nbSplits;
8312       i4 = i3;
8313     }
8314     else if ( nbFaceNodes == 4 ) {
8315       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8316       double aBestRate = DBL_MAX;
8317       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8318         i1 = 0; i2 = 1;
8319         double aBadRate = 0;
8320         // evaluate elements quality
8321         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8322           if ( iSplit == iQuad ) {
8323             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8324                                    linkNodes[ i2++ ],
8325                                    nodes[ i3 ],
8326                                    nodes[ i4 ]);
8327             aBadRate += getBadRate( &quad, aCrit );
8328           }
8329           else {
8330             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8331                                    linkNodes[ i2++ ],
8332                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8333             aBadRate += getBadRate( &tria, aCrit );
8334           }
8335         }
8336         // choice
8337         if ( aBadRate < aBestRate ) {
8338           iBestQuad = iQuad;
8339           aBestRate = aBadRate;
8340         }
8341       }
8342     }
8343
8344     // create new elements
8345     int aShapeId = FindShape( theFace );
8346
8347     i1 = 0; i2 = 1;
8348     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8349       SMDS_MeshElement* newElem = 0;
8350       if ( iSplit == iBestQuad )
8351         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8352                                   linkNodes[ i2++ ],
8353                                   nodes[ i3 ],
8354                                   nodes[ i4 ]);
8355       else
8356         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8357                                   linkNodes[ i2++ ],
8358                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8359       myLastCreatedElems.Append(newElem);
8360       if ( aShapeId && newElem )
8361         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8362     }
8363
8364     // change nodes of theFace
8365     const SMDS_MeshNode* newNodes[ 4 ];
8366     newNodes[ 0 ] = linkNodes[ i1 ];
8367     newNodes[ 1 ] = linkNodes[ i2 ];
8368     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8369     newNodes[ 3 ] = nodes[ i4 ];
8370     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8371     const SMDS_MeshElement* newElem = 0;
8372     if (iSplit == iBestQuad)
8373       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8374     else
8375       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8376     myLastCreatedElems.Append(newElem);
8377     if ( aShapeId && newElem )
8378       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8379 } // end if(!theFace->IsQuadratic())
8380   else { // theFace is quadratic
8381     // we have to split theFace on simple triangles and one simple quadrangle
8382     int tmp = il1/2;
8383     int nbshift = tmp*2;
8384     // shift nodes in nodes[] by nbshift
8385     int i,j;
8386     for(i=0; i<nbshift; i++) {
8387       const SMDS_MeshNode* n = nodes[0];
8388       for(j=0; j<nbFaceNodes-1; j++) {
8389         nodes[j] = nodes[j+1];
8390       }
8391       nodes[nbFaceNodes-1] = n;
8392     }
8393     il1 = il1 - nbshift;
8394     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8395     //   n0      n1     n2    n0      n1     n2
8396     //     +-----+-----+        +-----+-----+
8397     //      \         /         |           |
8398     //       \       /          |           |
8399     //      n5+     +n3       n7+           +n3
8400     //         \   /            |           |
8401     //          \ /             |           |
8402     //           +              +-----+-----+
8403     //           n4           n6      n5     n4
8404
8405     // create new elements
8406     int aShapeId = FindShape( theFace );
8407
8408     int n1,n2,n3;
8409     if(nbFaceNodes==6) { // quadratic triangle
8410       SMDS_MeshElement* newElem =
8411         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8412       myLastCreatedElems.Append(newElem);
8413       if ( aShapeId && newElem )
8414         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8415       if(theFace->IsMediumNode(nodes[il1])) {
8416         // create quadrangle
8417         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8418         myLastCreatedElems.Append(newElem);
8419         if ( aShapeId && newElem )
8420           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8421         n1 = 1;
8422         n2 = 2;
8423         n3 = 3;
8424       }
8425       else {
8426         // create quadrangle
8427         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8428         myLastCreatedElems.Append(newElem);
8429         if ( aShapeId && newElem )
8430           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8431         n1 = 0;
8432         n2 = 1;
8433         n3 = 5;
8434       }
8435     }
8436     else { // nbFaceNodes==8 - quadratic quadrangle
8437       SMDS_MeshElement* newElem =
8438         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8439       myLastCreatedElems.Append(newElem);
8440       if ( aShapeId && newElem )
8441         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8442       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8443       myLastCreatedElems.Append(newElem);
8444       if ( aShapeId && newElem )
8445         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8446       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8447       myLastCreatedElems.Append(newElem);
8448       if ( aShapeId && newElem )
8449         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8450       if(theFace->IsMediumNode(nodes[il1])) {
8451         // create quadrangle
8452         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8453         myLastCreatedElems.Append(newElem);
8454         if ( aShapeId && newElem )
8455           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8456         n1 = 1;
8457         n2 = 2;
8458         n3 = 3;
8459       }
8460       else {
8461         // create quadrangle
8462         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8463         myLastCreatedElems.Append(newElem);
8464         if ( aShapeId && newElem )
8465           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8466         n1 = 0;
8467         n2 = 1;
8468         n3 = 7;
8469       }
8470     }
8471     // create needed triangles using n1,n2,n3 and inserted nodes
8472     int nbn = 2 + aNodesToInsert.size();
8473     //const SMDS_MeshNode* aNodes[nbn];
8474     vector<const SMDS_MeshNode*> aNodes(nbn);
8475     aNodes[0] = nodes[n1];
8476     aNodes[nbn-1] = nodes[n2];
8477     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8478     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8479       aNodes[iNode++] = *nIt;
8480     }
8481     for(i=1; i<nbn; i++) {
8482       SMDS_MeshElement* newElem =
8483         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8484       myLastCreatedElems.Append(newElem);
8485       if ( aShapeId && newElem )
8486         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8487     }
8488   }
8489   // remove old face
8490   aMesh->RemoveElement(theFace);
8491 }
8492
8493 //=======================================================================
8494 //function : UpdateVolumes
8495 //purpose  :
8496 //=======================================================================
8497 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8498                                       const SMDS_MeshNode*        theBetweenNode2,
8499                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8500 {
8501   myLastCreatedElems.Clear();
8502   myLastCreatedNodes.Clear();
8503
8504   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8505   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8506     const SMDS_MeshElement* elem = invElemIt->next();
8507
8508     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8509     SMDS_VolumeTool aVolume (elem);
8510     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8511       continue;
8512
8513     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8514     int iface, nbFaces = aVolume.NbFaces();
8515     vector<const SMDS_MeshNode *> poly_nodes;
8516     vector<int> quantities (nbFaces);
8517
8518     for (iface = 0; iface < nbFaces; iface++) {
8519       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8520       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8521       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8522
8523       for (int inode = 0; inode < nbFaceNodes; inode++) {
8524         poly_nodes.push_back(faceNodes[inode]);
8525
8526         if (nbInserted == 0) {
8527           if (faceNodes[inode] == theBetweenNode1) {
8528             if (faceNodes[inode + 1] == theBetweenNode2) {
8529               nbInserted = theNodesToInsert.size();
8530
8531               // add nodes to insert
8532               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8533               for (; nIt != theNodesToInsert.end(); nIt++) {
8534                 poly_nodes.push_back(*nIt);
8535               }
8536             }
8537           }
8538           else if (faceNodes[inode] == theBetweenNode2) {
8539             if (faceNodes[inode + 1] == theBetweenNode1) {
8540               nbInserted = theNodesToInsert.size();
8541
8542               // add nodes to insert in reversed order
8543               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8544               nIt--;
8545               for (; nIt != theNodesToInsert.begin(); nIt--) {
8546                 poly_nodes.push_back(*nIt);
8547               }
8548               poly_nodes.push_back(*nIt);
8549             }
8550           }
8551           else {
8552           }
8553         }
8554       }
8555       quantities[iface] = nbFaceNodes + nbInserted;
8556     }
8557
8558     // Replace or update the volume
8559     SMESHDS_Mesh *aMesh = GetMeshDS();
8560
8561     if (elem->IsPoly()) {
8562       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8563
8564     }
8565     else {
8566       int aShapeId = FindShape( elem );
8567
8568       SMDS_MeshElement* newElem =
8569         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8570       myLastCreatedElems.Append(newElem);
8571       if (aShapeId && newElem)
8572         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8573
8574       aMesh->RemoveElement(elem);
8575     }
8576   }
8577 }
8578
8579 namespace
8580 {
8581   //================================================================================
8582   /*!
8583    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8584    */
8585   //================================================================================
8586
8587   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8588                            vector<const SMDS_MeshNode *> & nodes,
8589                            vector<int> &                   nbNodeInFaces )
8590   {
8591     nodes.clear();
8592     nbNodeInFaces.clear();
8593     SMDS_VolumeTool vTool ( elem );
8594     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8595     {
8596       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8597       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8598       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8599     }
8600   }
8601 }
8602
8603 //=======================================================================
8604 /*!
8605  * \brief Convert elements contained in a submesh to quadratic
8606  * \return int - nb of checked elements
8607  */
8608 //=======================================================================
8609
8610 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8611                                              SMESH_MesherHelper& theHelper,
8612                                              const bool          theForce3d)
8613 {
8614   int nbElem = 0;
8615   if( !theSm ) return nbElem;
8616
8617   vector<int> nbNodeInFaces;
8618   vector<const SMDS_MeshNode *> nodes;
8619   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8620   while(ElemItr->more())
8621   {
8622     nbElem++;
8623     const SMDS_MeshElement* elem = ElemItr->next();
8624     if( !elem ) continue;
8625
8626     // analyse a necessity of conversion
8627     const SMDSAbs_ElementType aType = elem->GetType();
8628     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8629       continue;
8630     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8631     bool hasCentralNodes = false;
8632     if ( elem->IsQuadratic() )
8633     {
8634       bool alreadyOK;
8635       switch ( aGeomType ) {
8636       case SMDSEntity_Quad_Triangle:
8637       case SMDSEntity_Quad_Quadrangle:
8638       case SMDSEntity_Quad_Hexa:
8639         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8640
8641       case SMDSEntity_BiQuad_Triangle:
8642       case SMDSEntity_BiQuad_Quadrangle:
8643       case SMDSEntity_TriQuad_Hexa:
8644         alreadyOK = theHelper.GetIsBiQuadratic();
8645         hasCentralNodes = true;
8646         break;
8647       default:
8648         alreadyOK = true;
8649       }
8650       // take into account already present modium nodes
8651       switch ( aType ) {
8652       case SMDSAbs_Volume:
8653         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8654       case SMDSAbs_Face:
8655         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8656       case SMDSAbs_Edge:
8657         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8658       default:;
8659       }
8660       if ( alreadyOK )
8661         continue;
8662     }
8663     // get elem data needed to re-create it
8664     //
8665     const int id      = elem->GetID();
8666     const int nbNodes = elem->NbCornerNodes();
8667     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8668     if ( aGeomType == SMDSEntity_Polyhedra )
8669       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8670     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8671       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8672
8673     // remove a linear element
8674     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8675
8676     // remove central nodes of biquadratic elements (biquad->quad convertion)
8677     if ( hasCentralNodes )
8678       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8679         if ( nodes[i]->NbInverseElements() == 0 )
8680           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8681
8682     const SMDS_MeshElement* NewElem = 0;
8683
8684     switch( aType )
8685     {
8686     case SMDSAbs_Edge :
8687       {
8688         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8689         break;
8690       }
8691     case SMDSAbs_Face :
8692       {
8693         switch(nbNodes)
8694         {
8695         case 3:
8696           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8697           break;
8698         case 4:
8699           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8700           break;
8701         default:
8702           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8703         }
8704         break;
8705       }
8706     case SMDSAbs_Volume :
8707       {
8708         switch( aGeomType )
8709         {
8710         case SMDSEntity_Tetra:
8711           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8712           break;
8713         case SMDSEntity_Pyramid:
8714           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8715           break;
8716         case SMDSEntity_Penta:
8717           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8718           break;
8719         case SMDSEntity_Hexa:
8720         case SMDSEntity_Quad_Hexa:
8721         case SMDSEntity_TriQuad_Hexa:
8722           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8723                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8724           break;
8725         case SMDSEntity_Hexagonal_Prism:
8726         default:
8727           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8728         }
8729         break;
8730       }
8731     default :
8732       continue;
8733     }
8734     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8735     if( NewElem && NewElem->getshapeId() < 1 )
8736       theSm->AddElement( NewElem );
8737   }
8738   return nbElem;
8739 }
8740 //=======================================================================
8741 //function : ConvertToQuadratic
8742 //purpose  :
8743 //=======================================================================
8744
8745 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8746 {
8747   SMESHDS_Mesh* meshDS = GetMeshDS();
8748
8749   SMESH_MesherHelper aHelper(*myMesh);
8750
8751   aHelper.SetIsQuadratic( true );
8752   aHelper.SetIsBiQuadratic( theToBiQuad );
8753   aHelper.SetElementsOnShape(true);
8754
8755   // convert elements assigned to sub-meshes
8756   int nbCheckedElems = 0;
8757   if ( myMesh->HasShapeToMesh() )
8758   {
8759     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8760     {
8761       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8762       while ( smIt->more() ) {
8763         SMESH_subMesh* sm = smIt->next();
8764         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8765           aHelper.SetSubShape( sm->GetSubShape() );
8766           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8767         }
8768       }
8769     }
8770   }
8771
8772   // convert elements NOT assigned to sub-meshes
8773   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8774   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8775   {
8776     aHelper.SetElementsOnShape(false);
8777     SMESHDS_SubMesh *smDS = 0;
8778
8779     // convert edges
8780     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8781     while( aEdgeItr->more() )
8782     {
8783       const SMDS_MeshEdge* edge = aEdgeItr->next();
8784       if ( !edge->IsQuadratic() )
8785       {
8786         int                  id = edge->GetID();
8787         const SMDS_MeshNode* n1 = edge->GetNode(0);
8788         const SMDS_MeshNode* n2 = edge->GetNode(1);
8789
8790         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8791
8792         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8793         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8794       }
8795       else
8796       {
8797         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8798       }
8799     }
8800
8801     // convert faces
8802     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8803     while( aFaceItr->more() )
8804     {
8805       const SMDS_MeshFace* face = aFaceItr->next();
8806       if ( !face ) continue;
8807       
8808       const SMDSAbs_EntityType type = face->GetEntityType();
8809       bool alreadyOK;
8810       switch( type )
8811       {
8812       case SMDSEntity_Quad_Triangle:
8813       case SMDSEntity_Quad_Quadrangle:
8814         alreadyOK = !theToBiQuad;
8815         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8816         break;
8817       case SMDSEntity_BiQuad_Triangle:
8818       case SMDSEntity_BiQuad_Quadrangle:
8819         alreadyOK = theToBiQuad;
8820         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8821         break;
8822       default: alreadyOK = false;
8823       }
8824       if ( alreadyOK )
8825         continue;
8826
8827       const int id = face->GetID();
8828       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8829
8830       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8831
8832       SMDS_MeshFace * NewFace = 0;
8833       switch( type )
8834       {
8835       case SMDSEntity_Triangle:
8836       case SMDSEntity_Quad_Triangle:
8837       case SMDSEntity_BiQuad_Triangle:
8838         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8839         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8840           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8841         break;
8842
8843       case SMDSEntity_Quadrangle:
8844       case SMDSEntity_Quad_Quadrangle:
8845       case SMDSEntity_BiQuad_Quadrangle:
8846         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8847         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8848           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8849         break;
8850
8851       default:;
8852         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8853       }
8854       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8855     }
8856
8857     // convert volumes
8858     vector<int> nbNodeInFaces;
8859     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8860     while(aVolumeItr->more())
8861     {
8862       const SMDS_MeshVolume* volume = aVolumeItr->next();
8863       if ( !volume ) continue;
8864
8865       const SMDSAbs_EntityType type = volume->GetEntityType();
8866       if ( volume->IsQuadratic() )
8867       {
8868         bool alreadyOK;
8869         switch ( type )
8870         {
8871         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8872         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8873         default:                      alreadyOK = true;
8874         }
8875         if ( alreadyOK )
8876         {
8877           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8878           continue;
8879         }
8880       }
8881       const int id = volume->GetID();
8882       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8883       if ( type == SMDSEntity_Polyhedra )
8884         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8885       else if ( type == SMDSEntity_Hexagonal_Prism )
8886         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8887
8888       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8889
8890       SMDS_MeshVolume * NewVolume = 0;
8891       switch ( type )
8892       {
8893       case SMDSEntity_Tetra:
8894         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8895         break;
8896       case SMDSEntity_Hexa:
8897       case SMDSEntity_Quad_Hexa:
8898       case SMDSEntity_TriQuad_Hexa:
8899         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8900                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8901         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8902           if ( nodes[i]->NbInverseElements() == 0 )
8903             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8904         break;
8905       case SMDSEntity_Pyramid:
8906         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8907                                       nodes[3], nodes[4], id, theForce3d);
8908         break;
8909       case SMDSEntity_Penta:
8910         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8911                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8912         break;
8913       case SMDSEntity_Hexagonal_Prism:
8914       default:
8915         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8916       }
8917       ReplaceElemInGroups(volume, NewVolume, meshDS);
8918     }
8919   }
8920
8921   if ( !theForce3d )
8922   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8923     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8924     // aHelper.FixQuadraticElements(myError);
8925     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8926   }
8927 }
8928
8929 //================================================================================
8930 /*!
8931  * \brief Makes given elements quadratic
8932  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8933  *  \param theElements - elements to make quadratic
8934  */
8935 //================================================================================
8936
8937 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8938                                           TIDSortedElemSet& theElements,
8939                                           const bool        theToBiQuad)
8940 {
8941   if ( theElements.empty() ) return;
8942
8943   // we believe that all theElements are of the same type
8944   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8945
8946   // get all nodes shared by theElements
8947   TIDSortedNodeSet allNodes;
8948   TIDSortedElemSet::iterator eIt = theElements.begin();
8949   for ( ; eIt != theElements.end(); ++eIt )
8950     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8951
8952   // complete theElements with elements of lower dim whose all nodes are in allNodes
8953
8954   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8955   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8956   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8957   for ( ; nIt != allNodes.end(); ++nIt )
8958   {
8959     const SMDS_MeshNode* n = *nIt;
8960     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8961     while ( invIt->more() )
8962     {
8963       const SMDS_MeshElement*      e = invIt->next();
8964       const SMDSAbs_ElementType type = e->GetType();
8965       if ( e->IsQuadratic() )
8966       {
8967         quadAdjacentElems[ type ].insert( e );
8968
8969         bool alreadyOK;
8970         switch ( e->GetEntityType() ) {
8971         case SMDSEntity_Quad_Triangle:
8972         case SMDSEntity_Quad_Quadrangle:
8973         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8974         case SMDSEntity_BiQuad_Triangle:
8975         case SMDSEntity_BiQuad_Quadrangle:
8976         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8977         default:                           alreadyOK = true;
8978         }
8979         if ( alreadyOK )
8980           continue;
8981       }
8982       if ( type >= elemType )
8983         continue; // same type or more complex linear element
8984
8985       if ( !checkedAdjacentElems[ type ].insert( e ).second )
8986         continue; // e is already checked
8987
8988       // check nodes
8989       bool allIn = true;
8990       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8991       while ( nodeIt->more() && allIn )
8992         allIn = allNodes.count( nodeIt->next() );
8993       if ( allIn )
8994         theElements.insert(e );
8995     }
8996   }
8997
8998   SMESH_MesherHelper helper(*myMesh);
8999   helper.SetIsQuadratic( true );
9000   helper.SetIsBiQuadratic( theToBiQuad );
9001
9002   // add links of quadratic adjacent elements to the helper
9003
9004   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9005     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9006           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9007     {
9008       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9009     }
9010   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9011     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9012           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9013     {
9014       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9015     }
9016   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9017     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9018           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9019     {
9020       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9021     }
9022
9023   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9024
9025   SMESHDS_Mesh*  meshDS = GetMeshDS();
9026   SMESHDS_SubMesh* smDS = 0;
9027   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9028   {
9029     const SMDS_MeshElement* elem = *eIt;
9030
9031     bool alreadyOK;
9032     int nbCentralNodes = 0;
9033     switch ( elem->GetEntityType() ) {
9034       // linear convertible
9035     case SMDSEntity_Edge:
9036     case SMDSEntity_Triangle:
9037     case SMDSEntity_Quadrangle:
9038     case SMDSEntity_Tetra:
9039     case SMDSEntity_Pyramid:
9040     case SMDSEntity_Hexa:
9041     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9042       // quadratic that can become bi-quadratic
9043     case SMDSEntity_Quad_Triangle:
9044     case SMDSEntity_Quad_Quadrangle:
9045     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9046       // bi-quadratic
9047     case SMDSEntity_BiQuad_Triangle:
9048     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9049     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9050       // the rest
9051     default:                           alreadyOK = true;
9052     }
9053     if ( alreadyOK ) continue;
9054
9055     const SMDSAbs_ElementType type = elem->GetType();
9056     const int                   id = elem->GetID();
9057     const int              nbNodes = elem->NbCornerNodes();
9058     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9059
9060     helper.SetSubShape( elem->getshapeId() );
9061
9062     if ( !smDS || !smDS->Contains( elem ))
9063       smDS = meshDS->MeshElements( elem->getshapeId() );
9064     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9065
9066     SMDS_MeshElement * newElem = 0;
9067     switch( nbNodes )
9068     {
9069     case 4: // cases for most frequently used element types go first (for optimization)
9070       if ( type == SMDSAbs_Volume )
9071         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9072       else
9073         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9074       break;
9075     case 8:
9076       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9077                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9078       break;
9079     case 3:
9080       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9081       break;
9082     case 2:
9083       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9084       break;
9085     case 5:
9086       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9087                                  nodes[4], id, theForce3d);
9088       break;
9089     case 6:
9090       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9091                                  nodes[4], nodes[5], id, theForce3d);
9092       break;
9093     default:;
9094     }
9095     ReplaceElemInGroups( elem, newElem, meshDS);
9096     if( newElem && smDS )
9097       smDS->AddElement( newElem );
9098
9099      // remove central nodes
9100     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9101       if ( nodes[i]->NbInverseElements() == 0 )
9102         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9103
9104   } // loop on theElements
9105
9106   if ( !theForce3d )
9107   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9108     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9109     // helper.FixQuadraticElements( myError );
9110     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9111   }
9112 }
9113
9114 //=======================================================================
9115 /*!
9116  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9117  * \return int - nb of checked elements
9118  */
9119 //=======================================================================
9120
9121 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9122                                      SMDS_ElemIteratorPtr theItr,
9123                                      const int            theShapeID)
9124 {
9125   int nbElem = 0;
9126   SMESHDS_Mesh* meshDS = GetMeshDS();
9127
9128   while( theItr->more() )
9129   {
9130     const SMDS_MeshElement* elem = theItr->next();
9131     nbElem++;
9132     if( elem && elem->IsQuadratic())
9133     {
9134       int id                    = elem->GetID();
9135       int nbCornerNodes         = elem->NbCornerNodes();
9136       SMDSAbs_ElementType aType = elem->GetType();
9137
9138       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9139
9140       //remove a quadratic element
9141       if ( !theSm || !theSm->Contains( elem ))
9142         theSm = meshDS->MeshElements( elem->getshapeId() );
9143       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9144
9145       // remove medium nodes
9146       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9147         if ( nodes[i]->NbInverseElements() == 0 )
9148           meshDS->RemoveFreeNode( nodes[i], theSm );
9149
9150       // add a linear element
9151       nodes.resize( nbCornerNodes );
9152       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9153       ReplaceElemInGroups(elem, newElem, meshDS);
9154       if( theSm && newElem )
9155         theSm->AddElement( newElem );
9156     }
9157   }
9158   return nbElem;
9159 }
9160
9161 //=======================================================================
9162 //function : ConvertFromQuadratic
9163 //purpose  :
9164 //=======================================================================
9165
9166 bool SMESH_MeshEditor::ConvertFromQuadratic()
9167 {
9168   int nbCheckedElems = 0;
9169   if ( myMesh->HasShapeToMesh() )
9170   {
9171     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9172     {
9173       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9174       while ( smIt->more() ) {
9175         SMESH_subMesh* sm = smIt->next();
9176         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9177           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9178       }
9179     }
9180   }
9181
9182   int totalNbElems =
9183     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9184   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9185   {
9186     SMESHDS_SubMesh *aSM = 0;
9187     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9188   }
9189
9190   return true;
9191 }
9192
9193 namespace
9194 {
9195   //================================================================================
9196   /*!
9197    * \brief Return true if all medium nodes of the element are in the node set
9198    */
9199   //================================================================================
9200
9201   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9202   {
9203     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9204       if ( !nodeSet.count( elem->GetNode(i) ))
9205         return false;
9206     return true;
9207   }
9208 }
9209
9210 //================================================================================
9211 /*!
9212  * \brief Makes given elements linear
9213  */
9214 //================================================================================
9215
9216 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9217 {
9218   if ( theElements.empty() ) return;
9219
9220   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9221   set<int> mediumNodeIDs;
9222   TIDSortedElemSet::iterator eIt = theElements.begin();
9223   for ( ; eIt != theElements.end(); ++eIt )
9224   {
9225     const SMDS_MeshElement* e = *eIt;
9226     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9227       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9228   }
9229
9230   // replace given elements by linear ones
9231   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9232   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9233
9234   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9235   // except those elements sharing medium nodes of quadratic element whose medium nodes
9236   // are not all in mediumNodeIDs
9237
9238   // get remaining medium nodes
9239   TIDSortedNodeSet mediumNodes;
9240   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9241   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9242     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9243       mediumNodes.insert( mediumNodes.end(), n );
9244
9245   // find more quadratic elements to convert
9246   TIDSortedElemSet moreElemsToConvert;
9247   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9248   for ( ; nIt != mediumNodes.end(); ++nIt )
9249   {
9250     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9251     while ( invIt->more() )
9252     {
9253       const SMDS_MeshElement* e = invIt->next();
9254       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9255       {
9256         // find a more complex element including e and
9257         // whose medium nodes are not in mediumNodes
9258         bool complexFound = false;
9259         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9260         {
9261           SMDS_ElemIteratorPtr invIt2 =
9262             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9263           while ( invIt2->more() )
9264           {
9265             const SMDS_MeshElement* eComplex = invIt2->next();
9266             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9267             {
9268               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9269               if ( nbCommonNodes == e->NbNodes())
9270               {
9271                 complexFound = true;
9272                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9273                 break;
9274               }
9275             }
9276           }
9277         }
9278         if ( !complexFound )
9279           moreElemsToConvert.insert( e );
9280       }
9281     }
9282   }
9283   elemIt = elemSetIterator( moreElemsToConvert );
9284   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9285 }
9286
9287 //=======================================================================
9288 //function : SewSideElements
9289 //purpose  :
9290 //=======================================================================
9291
9292 SMESH_MeshEditor::Sew_Error
9293 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9294                                    TIDSortedElemSet&    theSide2,
9295                                    const SMDS_MeshNode* theFirstNode1,
9296                                    const SMDS_MeshNode* theFirstNode2,
9297                                    const SMDS_MeshNode* theSecondNode1,
9298                                    const SMDS_MeshNode* theSecondNode2)
9299 {
9300   myLastCreatedElems.Clear();
9301   myLastCreatedNodes.Clear();
9302
9303   MESSAGE ("::::SewSideElements()");
9304   if ( theSide1.size() != theSide2.size() )
9305     return SEW_DIFF_NB_OF_ELEMENTS;
9306
9307   Sew_Error aResult = SEW_OK;
9308   // Algo:
9309   // 1. Build set of faces representing each side
9310   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9311   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9312
9313   // =======================================================================
9314   // 1. Build set of faces representing each side:
9315   // =======================================================================
9316   // a. build set of nodes belonging to faces
9317   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9318   // c. create temporary faces representing side of volumes if correspondent
9319   //    face does not exist
9320
9321   SMESHDS_Mesh* aMesh = GetMeshDS();
9322   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9323   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9324   TIDSortedElemSet             faceSet1, faceSet2;
9325   set<const SMDS_MeshElement*> volSet1,  volSet2;
9326   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9327   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9328   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9329   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9330   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9331   int iSide, iFace, iNode;
9332
9333   list<const SMDS_MeshElement* > tempFaceList;
9334   for ( iSide = 0; iSide < 2; iSide++ ) {
9335     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9336     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9337     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9338     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9339     set<const SMDS_MeshElement*>::iterator vIt;
9340     TIDSortedElemSet::iterator eIt;
9341     set<const SMDS_MeshNode*>::iterator    nIt;
9342
9343     // check that given nodes belong to given elements
9344     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9345     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9346     int firstIndex = -1, secondIndex = -1;
9347     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9348       const SMDS_MeshElement* elem = *eIt;
9349       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9350       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9351       if ( firstIndex > -1 && secondIndex > -1 ) break;
9352     }
9353     if ( firstIndex < 0 || secondIndex < 0 ) {
9354       // we can simply return until temporary faces created
9355       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9356     }
9357
9358     // -----------------------------------------------------------
9359     // 1a. Collect nodes of existing faces
9360     //     and build set of face nodes in order to detect missing
9361     //     faces corresponding to sides of volumes
9362     // -----------------------------------------------------------
9363
9364     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9365
9366     // loop on the given element of a side
9367     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9368       //const SMDS_MeshElement* elem = *eIt;
9369       const SMDS_MeshElement* elem = *eIt;
9370       if ( elem->GetType() == SMDSAbs_Face ) {
9371         faceSet->insert( elem );
9372         set <const SMDS_MeshNode*> faceNodeSet;
9373         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9374         while ( nodeIt->more() ) {
9375           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9376           nodeSet->insert( n );
9377           faceNodeSet.insert( n );
9378         }
9379         setOfFaceNodeSet.insert( faceNodeSet );
9380       }
9381       else if ( elem->GetType() == SMDSAbs_Volume )
9382         volSet->insert( elem );
9383     }
9384     // ------------------------------------------------------------------------------
9385     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9386     // ------------------------------------------------------------------------------
9387
9388     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9389       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9390       while ( fIt->more() ) { // loop on faces sharing a node
9391         const SMDS_MeshElement* f = fIt->next();
9392         if ( faceSet->find( f ) == faceSet->end() ) {
9393           // check if all nodes are in nodeSet and
9394           // complete setOfFaceNodeSet if they are
9395           set <const SMDS_MeshNode*> faceNodeSet;
9396           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9397           bool allInSet = true;
9398           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9399             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9400             if ( nodeSet->find( n ) == nodeSet->end() )
9401               allInSet = false;
9402             else
9403               faceNodeSet.insert( n );
9404           }
9405           if ( allInSet ) {
9406             faceSet->insert( f );
9407             setOfFaceNodeSet.insert( faceNodeSet );
9408           }
9409         }
9410       }
9411     }
9412
9413     // -------------------------------------------------------------------------
9414     // 1c. Create temporary faces representing sides of volumes if correspondent
9415     //     face does not exist
9416     // -------------------------------------------------------------------------
9417
9418     if ( !volSet->empty() ) {
9419       //int nodeSetSize = nodeSet->size();
9420
9421       // loop on given volumes
9422       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9423         SMDS_VolumeTool vol (*vIt);
9424         // loop on volume faces: find free faces
9425         // --------------------------------------
9426         list<const SMDS_MeshElement* > freeFaceList;
9427         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9428           if ( !vol.IsFreeFace( iFace ))
9429             continue;
9430           // check if there is already a face with same nodes in a face set
9431           const SMDS_MeshElement* aFreeFace = 0;
9432           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9433           int nbNodes = vol.NbFaceNodes( iFace );
9434           set <const SMDS_MeshNode*> faceNodeSet;
9435           vol.GetFaceNodes( iFace, faceNodeSet );
9436           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9437           if ( isNewFace ) {
9438             // no such a face is given but it still can exist, check it
9439             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9440             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9441           }
9442           if ( !aFreeFace ) {
9443             // create a temporary face
9444             if ( nbNodes == 3 ) {
9445               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9446               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9447             }
9448             else if ( nbNodes == 4 ) {
9449               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9450               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9451             }
9452             else {
9453               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9454               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9455               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9456             }
9457             if ( aFreeFace )
9458               tempFaceList.push_back( aFreeFace );
9459           }
9460
9461           if ( aFreeFace )
9462             freeFaceList.push_back( aFreeFace );
9463
9464         } // loop on faces of a volume
9465
9466         // choose one of several free faces of a volume
9467         // --------------------------------------------
9468         if ( freeFaceList.size() > 1 ) {
9469           // choose a face having max nb of nodes shared by other elems of a side
9470           int maxNbNodes = -1;
9471           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9472           while ( fIt != freeFaceList.end() ) { // loop on free faces
9473             int nbSharedNodes = 0;
9474             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9475             while ( nodeIt->more() ) { // loop on free face nodes
9476               const SMDS_MeshNode* n =
9477                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9478               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9479               while ( invElemIt->more() ) {
9480                 const SMDS_MeshElement* e = invElemIt->next();
9481                 nbSharedNodes += faceSet->count( e );
9482                 nbSharedNodes += elemSet->count( e );
9483               }
9484             }
9485             if ( nbSharedNodes > maxNbNodes ) {
9486               maxNbNodes = nbSharedNodes;
9487               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9488             }
9489             else if ( nbSharedNodes == maxNbNodes ) {
9490               fIt++;
9491             }
9492             else {
9493               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9494             }
9495           }
9496           if ( freeFaceList.size() > 1 )
9497           {
9498             // could not choose one face, use another way
9499             // choose a face most close to the bary center of the opposite side
9500             gp_XYZ aBC( 0., 0., 0. );
9501             set <const SMDS_MeshNode*> addedNodes;
9502             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9503             eIt = elemSet2->begin();
9504             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9505               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9506               while ( nodeIt->more() ) { // loop on free face nodes
9507                 const SMDS_MeshNode* n =
9508                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9509                 if ( addedNodes.insert( n ).second )
9510                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9511               }
9512             }
9513             aBC /= addedNodes.size();
9514             double minDist = DBL_MAX;
9515             fIt = freeFaceList.begin();
9516             while ( fIt != freeFaceList.end() ) { // loop on free faces
9517               double dist = 0;
9518               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9519               while ( nodeIt->more() ) { // loop on free face nodes
9520                 const SMDS_MeshNode* n =
9521                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9523                 dist += ( aBC - p ).SquareModulus();
9524               }
9525               if ( dist < minDist ) {
9526                 minDist = dist;
9527                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9528               }
9529               else
9530                 fIt = freeFaceList.erase( fIt++ );
9531             }
9532           }
9533         } // choose one of several free faces of a volume
9534
9535         if ( freeFaceList.size() == 1 ) {
9536           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9537           faceSet->insert( aFreeFace );
9538           // complete a node set with nodes of a found free face
9539           //           for ( iNode = 0; iNode < ; iNode++ )
9540           //             nodeSet->insert( fNodes[ iNode ] );
9541         }
9542
9543       } // loop on volumes of a side
9544
9545       //       // complete a set of faces if new nodes in a nodeSet appeared
9546       //       // ----------------------------------------------------------
9547       //       if ( nodeSetSize != nodeSet->size() ) {
9548       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9549       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9550       //           while ( fIt->more() ) { // loop on faces sharing a node
9551       //             const SMDS_MeshElement* f = fIt->next();
9552       //             if ( faceSet->find( f ) == faceSet->end() ) {
9553       //               // check if all nodes are in nodeSet and
9554       //               // complete setOfFaceNodeSet if they are
9555       //               set <const SMDS_MeshNode*> faceNodeSet;
9556       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9557       //               bool allInSet = true;
9558       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9559       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9561       //                   allInSet = false;
9562       //                 else
9563       //                   faceNodeSet.insert( n );
9564       //               }
9565       //               if ( allInSet ) {
9566       //                 faceSet->insert( f );
9567       //                 setOfFaceNodeSet.insert( faceNodeSet );
9568       //               }
9569       //             }
9570       //           }
9571       //         }
9572       //       }
9573     } // Create temporary faces, if there are volumes given
9574   } // loop on sides
9575
9576   if ( faceSet1.size() != faceSet2.size() ) {
9577     // delete temporary faces: they are in reverseElements of actual nodes
9578 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9579 //    while ( tmpFaceIt->more() )
9580 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9581 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9582 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9583 //      aMesh->RemoveElement(*tmpFaceIt);
9584     MESSAGE("Diff nb of faces");
9585     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9586   }
9587
9588   // ============================================================
9589   // 2. Find nodes to merge:
9590   //              bind a node to remove to a node to put instead
9591   // ============================================================
9592
9593   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9594   if ( theFirstNode1 != theFirstNode2 )
9595     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9596   if ( theSecondNode1 != theSecondNode2 )
9597     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9598
9599   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9600   set< long > linkIdSet; // links to process
9601   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9602
9603   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9604   list< NLink > linkList[2];
9605   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9606   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9607   // loop on links in linkList; find faces by links and append links
9608   // of the found faces to linkList
9609   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9610   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9611   {
9612     NLink link[] = { *linkIt[0], *linkIt[1] };
9613     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9614     if ( !linkIdSet.count( linkID ) )
9615       continue;
9616
9617     // by links, find faces in the face sets,
9618     // and find indices of link nodes in the found faces;
9619     // in a face set, there is only one or no face sharing a link
9620     // ---------------------------------------------------------------
9621
9622     const SMDS_MeshElement* face[] = { 0, 0 };
9623     vector<const SMDS_MeshNode*> fnodes[2];
9624     int iLinkNode[2][2];
9625     TIDSortedElemSet avoidSet;
9626     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9627       const SMDS_MeshNode* n1 = link[iSide].first;
9628       const SMDS_MeshNode* n2 = link[iSide].second;
9629       //cout << "Side " << iSide << " ";
9630       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9631       // find a face by two link nodes
9632       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9633                                                       *faceSetPtr[ iSide ], avoidSet,
9634                                                       &iLinkNode[iSide][0],
9635                                                       &iLinkNode[iSide][1] );
9636       if ( face[ iSide ])
9637       {
9638         //cout << " F " << face[ iSide]->GetID() <<endl;
9639         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9640         // put face nodes to fnodes
9641         if ( face[ iSide ]->IsQuadratic() )
9642         {
9643           // use interlaced nodes iterator
9644           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9645           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9646           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9647           while ( nIter->more() )
9648             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9649         }
9650         else
9651         {
9652           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9653                                   face[ iSide ]->end_nodes() );
9654         }
9655         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9656       }
9657     }
9658
9659     // check similarity of elements of the sides
9660     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9661       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9662       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9663         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9664       }
9665       else {
9666         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9667       }
9668       break; // do not return because it's necessary to remove tmp faces
9669     }
9670
9671     // set nodes to merge
9672     // -------------------
9673
9674     if ( face[0] && face[1] )  {
9675       const int nbNodes = face[0]->NbNodes();
9676       if ( nbNodes != face[1]->NbNodes() ) {
9677         MESSAGE("Diff nb of face nodes");
9678         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9679         break; // do not return because it s necessary to remove tmp faces
9680       }
9681       bool reverse[] = { false, false }; // order of nodes in the link
9682       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9683         // analyse link orientation in faces
9684         int i1 = iLinkNode[ iSide ][ 0 ];
9685         int i2 = iLinkNode[ iSide ][ 1 ];
9686         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9687       }
9688       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9689       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9690       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9691       {
9692         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9693                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9694       }
9695
9696       // add other links of the faces to linkList
9697       // -----------------------------------------
9698
9699       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9700         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9701         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9702         if ( !iter_isnew.second ) { // already in a set: no need to process
9703           linkIdSet.erase( iter_isnew.first );
9704         }
9705         else // new in set == encountered for the first time: add
9706         {
9707           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9708           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9709           linkList[0].push_back ( NLink( n1, n2 ));
9710           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9711         }
9712       }
9713     } // 2 faces found
9714
9715     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9716       break;
9717
9718   } // loop on link lists
9719
9720   if ( aResult == SEW_OK &&
9721        ( //linkIt[0] != linkList[0].end() ||
9722          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9723     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9724              " " << (faceSetPtr[1]->empty()));
9725     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9726   }
9727
9728   // ====================================================================
9729   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9730   // ====================================================================
9731
9732   // delete temporary faces
9733 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9734 //  while ( tmpFaceIt->more() )
9735 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9736   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9737   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9738     aMesh->RemoveElement(*tmpFaceIt);
9739
9740   if ( aResult != SEW_OK)
9741     return aResult;
9742
9743   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9744   // loop on nodes replacement map
9745   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9746   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9747     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9748       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9749       nodeIDsToRemove.push_back( nToRemove->GetID() );
9750       // loop on elements sharing nToRemove
9751       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9752       while ( invElemIt->more() ) {
9753         const SMDS_MeshElement* e = invElemIt->next();
9754         // get a new suite of nodes: make replacement
9755         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9756         vector< const SMDS_MeshNode*> nodes( nbNodes );
9757         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9758         while ( nIt->more() ) {
9759           const SMDS_MeshNode* n =
9760             static_cast<const SMDS_MeshNode*>( nIt->next() );
9761           nnIt = nReplaceMap.find( n );
9762           if ( nnIt != nReplaceMap.end() ) {
9763             nbReplaced++;
9764             n = (*nnIt).second;
9765           }
9766           nodes[ i++ ] = n;
9767         }
9768         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9769         //         elemIDsToRemove.push_back( e->GetID() );
9770         //       else
9771         if ( nbReplaced )
9772           {
9773             SMDSAbs_ElementType etyp = e->GetType();
9774             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9775             if (newElem)
9776               {
9777                 myLastCreatedElems.Append(newElem);
9778                 AddToSameGroups(newElem, e, aMesh);
9779                 int aShapeId = e->getshapeId();
9780                 if ( aShapeId )
9781                   {
9782                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9783                   }
9784               }
9785             aMesh->RemoveElement(e);
9786           }
9787       }
9788     }
9789
9790   Remove( nodeIDsToRemove, true );
9791
9792   return aResult;
9793 }
9794
9795 //================================================================================
9796 /*!
9797  * \brief Find corresponding nodes in two sets of faces
9798  * \param theSide1 - first face set
9799  * \param theSide2 - second first face
9800  * \param theFirstNode1 - a boundary node of set 1
9801  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9802  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9803  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9804  * \param nReplaceMap - output map of corresponding nodes
9805  * \return bool  - is a success or not
9806  */
9807 //================================================================================
9808
9809 #ifdef _DEBUG_
9810 //#define DEBUG_MATCHING_NODES
9811 #endif
9812
9813 SMESH_MeshEditor::Sew_Error
9814 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9815                                     set<const SMDS_MeshElement*>& theSide2,
9816                                     const SMDS_MeshNode*          theFirstNode1,
9817                                     const SMDS_MeshNode*          theFirstNode2,
9818                                     const SMDS_MeshNode*          theSecondNode1,
9819                                     const SMDS_MeshNode*          theSecondNode2,
9820                                     TNodeNodeMap &                nReplaceMap)
9821 {
9822   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9823
9824   nReplaceMap.clear();
9825   if ( theFirstNode1 != theFirstNode2 )
9826     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9827   if ( theSecondNode1 != theSecondNode2 )
9828     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9829
9830   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9831   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9832
9833   list< NLink > linkList[2];
9834   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9835   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9836
9837   // loop on links in linkList; find faces by links and append links
9838   // of the found faces to linkList
9839   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9840   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9841     NLink link[] = { *linkIt[0], *linkIt[1] };
9842     if ( linkSet.find( link[0] ) == linkSet.end() )
9843       continue;
9844
9845     // by links, find faces in the face sets,
9846     // and find indices of link nodes in the found faces;
9847     // in a face set, there is only one or no face sharing a link
9848     // ---------------------------------------------------------------
9849
9850     const SMDS_MeshElement* face[] = { 0, 0 };
9851     list<const SMDS_MeshNode*> notLinkNodes[2];
9852     //bool reverse[] = { false, false }; // order of notLinkNodes
9853     int nbNodes[2];
9854     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9855     {
9856       const SMDS_MeshNode* n1 = link[iSide].first;
9857       const SMDS_MeshNode* n2 = link[iSide].second;
9858       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9859       set< const SMDS_MeshElement* > facesOfNode1;
9860       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9861       {
9862         // during a loop of the first node, we find all faces around n1,
9863         // during a loop of the second node, we find one face sharing both n1 and n2
9864         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9865         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9866         while ( fIt->more() ) { // loop on faces sharing a node
9867           const SMDS_MeshElement* f = fIt->next();
9868           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9869               ! facesOfNode1.insert( f ).second ) // f encounters twice
9870           {
9871             if ( face[ iSide ] ) {
9872               MESSAGE( "2 faces per link " );
9873               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9874             }
9875             face[ iSide ] = f;
9876             faceSet->erase( f );
9877
9878             // get not link nodes
9879             int nbN = f->NbNodes();
9880             if ( f->IsQuadratic() )
9881               nbN /= 2;
9882             nbNodes[ iSide ] = nbN;
9883             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9884             int i1 = f->GetNodeIndex( n1 );
9885             int i2 = f->GetNodeIndex( n2 );
9886             int iEnd = nbN, iBeg = -1, iDelta = 1;
9887             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9888             if ( reverse ) {
9889               std::swap( iEnd, iBeg ); iDelta = -1;
9890             }
9891             int i = i2;
9892             while ( true ) {
9893               i += iDelta;
9894               if ( i == iEnd ) i = iBeg + iDelta;
9895               if ( i == i1 ) break;
9896               nodes.push_back ( f->GetNode( i ) );
9897             }
9898           }
9899         }
9900       }
9901     }
9902     // check similarity of elements of the sides
9903     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9904       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9905       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9906         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9907       }
9908       else {
9909         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9910       }
9911     }
9912
9913     // set nodes to merge
9914     // -------------------
9915
9916     if ( face[0] && face[1] )  {
9917       if ( nbNodes[0] != nbNodes[1] ) {
9918         MESSAGE("Diff nb of face nodes");
9919         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9920       }
9921 #ifdef DEBUG_MATCHING_NODES
9922       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9923                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9924                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9925 #endif
9926       int nbN = nbNodes[0];
9927       {
9928         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9929         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9930         for ( int i = 0 ; i < nbN - 2; ++i ) {
9931 #ifdef DEBUG_MATCHING_NODES
9932           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9933 #endif
9934           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9935         }
9936       }
9937
9938       // add other links of the face 1 to linkList
9939       // -----------------------------------------
9940
9941       const SMDS_MeshElement* f0 = face[0];
9942       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9943       for ( int i = 0; i < nbN; i++ )
9944       {
9945         const SMDS_MeshNode* n2 = f0->GetNode( i );
9946         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9947           linkSet.insert( SMESH_TLink( n1, n2 ));
9948         if ( !iter_isnew.second ) { // already in a set: no need to process
9949           linkSet.erase( iter_isnew.first );
9950         }
9951         else // new in set == encountered for the first time: add
9952         {
9953 #ifdef DEBUG_MATCHING_NODES
9954           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9955                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9956 #endif
9957           linkList[0].push_back ( NLink( n1, n2 ));
9958           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9959         }
9960         n1 = n2;
9961       }
9962     } // 2 faces found
9963   } // loop on link lists
9964
9965   return SEW_OK;
9966 }
9967
9968 //================================================================================
9969 /*!
9970  * \brief Create elements equal (on same nodes) to given ones
9971  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9972  *              elements of the uppest dimension are duplicated.
9973  */
9974 //================================================================================
9975
9976 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9977 {
9978   CrearLastCreated();
9979   SMESHDS_Mesh* mesh = GetMeshDS();
9980
9981   // get an element type and an iterator over elements
9982
9983   SMDSAbs_ElementType type;
9984   SMDS_ElemIteratorPtr elemIt;
9985   vector< const SMDS_MeshElement* > allElems;
9986   if ( theElements.empty() )
9987   {
9988     if ( mesh->NbNodes() == 0 )
9989       return;
9990     // get most complex type
9991     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9992       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9993       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9994     };
9995     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9996       if ( mesh->GetMeshInfo().NbElements( types[i] ))
9997       {
9998         type = types[i];
9999         break;
10000       }
10001     // put all elements in the vector <allElems>
10002     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10003     elemIt = mesh->elementsIterator( type );
10004     while ( elemIt->more() )
10005       allElems.push_back( elemIt->next());
10006     elemIt = elemSetIterator( allElems );
10007   }
10008   else
10009   {
10010     type = (*theElements.begin())->GetType();
10011     elemIt = elemSetIterator( theElements );
10012   }
10013
10014   // duplicate elements
10015
10016   if ( type == SMDSAbs_Ball )
10017   {
10018     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10019     while ( elemIt->more() )
10020     {
10021       const SMDS_MeshElement* elem = elemIt->next();
10022       if ( elem->GetType() != SMDSAbs_Ball )
10023         continue;
10024       if (( elem = mesh->AddBall( elem->GetNode(0),
10025                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10026         myLastCreatedElems.Append( elem );
10027     }
10028   }
10029   else
10030   {
10031     vector< const SMDS_MeshNode* > nodes;
10032     while ( elemIt->more() )
10033     {
10034       const SMDS_MeshElement* elem = elemIt->next();
10035       if ( elem->GetType() != type )
10036         continue;
10037
10038       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10039
10040       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10041       {
10042         std::vector<int> quantities =
10043           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10044         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10045       }
10046       else
10047       {
10048         AddElement( nodes, type, elem->IsPoly() );
10049         elem = 0; // myLastCreatedElems is already filled
10050       }
10051       if ( elem )
10052         myLastCreatedElems.Append( elem );
10053     }
10054   }
10055 }
10056
10057 //================================================================================
10058 /*!
10059   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10060   \param theElems - the list of elements (edges or faces) to be replicated
10061   The nodes for duplication could be found from these elements
10062   \param theNodesNot - list of nodes to NOT replicate
10063   \param theAffectedElems - the list of elements (cells and edges) to which the
10064   replicated nodes should be associated to.
10065   \return TRUE if operation has been completed successfully, FALSE otherwise
10066 */
10067 //================================================================================
10068
10069 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10070                                     const TIDSortedElemSet& theNodesNot,
10071                                     const TIDSortedElemSet& theAffectedElems )
10072 {
10073   myLastCreatedElems.Clear();
10074   myLastCreatedNodes.Clear();
10075
10076   if ( theElems.size() == 0 )
10077     return false;
10078
10079   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10080   if ( !aMeshDS )
10081     return false;
10082
10083   bool res = false;
10084   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10085   // duplicate elements and nodes
10086   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10087   // replce nodes by duplications
10088   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10089   return res;
10090 }
10091
10092 //================================================================================
10093 /*!
10094   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10095   \param theMeshDS - mesh instance
10096   \param theElems - the elements replicated or modified (nodes should be changed)
10097   \param theNodesNot - nodes to NOT replicate
10098   \param theNodeNodeMap - relation of old node to new created node
10099   \param theIsDoubleElem - flag os to replicate element or modify
10100   \return TRUE if operation has been completed successfully, FALSE otherwise
10101 */
10102 //================================================================================
10103
10104 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10105                                     const TIDSortedElemSet& theElems,
10106                                     const TIDSortedElemSet& theNodesNot,
10107                                     std::map< const SMDS_MeshNode*,
10108                                     const SMDS_MeshNode* >& theNodeNodeMap,
10109                                     const bool theIsDoubleElem )
10110 {
10111   MESSAGE("doubleNodes");
10112   // iterate on through element and duplicate them (by nodes duplication)
10113   bool res = false;
10114   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10115   for ( ;  elemItr != theElems.end(); ++elemItr )
10116   {
10117     const SMDS_MeshElement* anElem = *elemItr;
10118     if (!anElem)
10119       continue;
10120
10121     bool isDuplicate = false;
10122     // duplicate nodes to duplicate element
10123     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10124     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10125     int ind = 0;
10126     while ( anIter->more() )
10127     {
10128
10129       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10130       SMDS_MeshNode* aNewNode = aCurrNode;
10131       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10132         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10133       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10134       {
10135         // duplicate node
10136         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10137         theNodeNodeMap[ aCurrNode ] = aNewNode;
10138         myLastCreatedNodes.Append( aNewNode );
10139       }
10140       isDuplicate |= (aCurrNode != aNewNode);
10141       newNodes[ ind++ ] = aNewNode;
10142     }
10143     if ( !isDuplicate )
10144       continue;
10145
10146     if ( theIsDoubleElem )
10147       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10148     else
10149       {
10150       MESSAGE("ChangeElementNodes");
10151       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10152       }
10153     res = true;
10154   }
10155   return res;
10156 }
10157
10158 //================================================================================
10159 /*!
10160   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10161   \param theNodes - identifiers of nodes to be doubled
10162   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10163          nodes. If list of element identifiers is empty then nodes are doubled but
10164          they not assigned to elements
10165   \return TRUE if operation has been completed successfully, FALSE otherwise
10166 */
10167 //================================================================================
10168
10169 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10170                                     const std::list< int >& theListOfModifiedElems )
10171 {
10172   MESSAGE("DoubleNodes");
10173   myLastCreatedElems.Clear();
10174   myLastCreatedNodes.Clear();
10175
10176   if ( theListOfNodes.size() == 0 )
10177     return false;
10178
10179   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10180   if ( !aMeshDS )
10181     return false;
10182
10183   // iterate through nodes and duplicate them
10184
10185   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10186
10187   std::list< int >::const_iterator aNodeIter;
10188   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10189   {
10190     int aCurr = *aNodeIter;
10191     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10192     if ( !aNode )
10193       continue;
10194
10195     // duplicate node
10196
10197     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10198     if ( aNewNode )
10199     {
10200       anOldNodeToNewNode[ aNode ] = aNewNode;
10201       myLastCreatedNodes.Append( aNewNode );
10202     }
10203   }
10204
10205   // Create map of new nodes for modified elements
10206
10207   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10208
10209   std::list< int >::const_iterator anElemIter;
10210   for ( anElemIter = theListOfModifiedElems.begin();
10211         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10212   {
10213     int aCurr = *anElemIter;
10214     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10215     if ( !anElem )
10216       continue;
10217
10218     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10219
10220     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10221     int ind = 0;
10222     while ( anIter->more() )
10223     {
10224       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10225       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10226       {
10227         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10228         aNodeArr[ ind++ ] = aNewNode;
10229       }
10230       else
10231         aNodeArr[ ind++ ] = aCurrNode;
10232     }
10233     anElemToNodes[ anElem ] = aNodeArr;
10234   }
10235
10236   // Change nodes of elements
10237
10238   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10239     anElemToNodesIter = anElemToNodes.begin();
10240   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10241   {
10242     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10243     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10244     if ( anElem )
10245       {
10246       MESSAGE("ChangeElementNodes");
10247       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10248       }
10249   }
10250
10251   return true;
10252 }
10253
10254 namespace {
10255
10256   //================================================================================
10257   /*!
10258   \brief Check if element located inside shape
10259   \return TRUE if IN or ON shape, FALSE otherwise
10260   */
10261   //================================================================================
10262
10263   template<class Classifier>
10264   bool isInside(const SMDS_MeshElement* theElem,
10265                 Classifier&             theClassifier,
10266                 const double            theTol)
10267   {
10268     gp_XYZ centerXYZ (0, 0, 0);
10269     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10270     while (aNodeItr->more())
10271       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10272
10273     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10274     theClassifier.Perform(aPnt, theTol);
10275     TopAbs_State aState = theClassifier.State();
10276     return (aState == TopAbs_IN || aState == TopAbs_ON );
10277   }
10278
10279   //================================================================================
10280   /*!
10281    * \brief Classifier of the 3D point on the TopoDS_Face
10282    *        with interaface suitable for isInside()
10283    */
10284   //================================================================================
10285
10286   struct _FaceClassifier
10287   {
10288     Extrema_ExtPS       _extremum;
10289     BRepAdaptor_Surface _surface;
10290     TopAbs_State        _state;
10291
10292     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10293     {
10294       _extremum.Initialize( _surface,
10295                             _surface.FirstUParameter(), _surface.LastUParameter(),
10296                             _surface.FirstVParameter(), _surface.LastVParameter(),
10297                             _surface.Tolerance(), _surface.Tolerance() );
10298     }
10299     void Perform(const gp_Pnt& aPnt, double theTol)
10300     {
10301       _state = TopAbs_OUT;
10302       _extremum.Perform(aPnt);
10303       if ( _extremum.IsDone() )
10304         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10305 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10306           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10307 #else
10308           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10309 #endif
10310     }
10311     TopAbs_State State() const
10312     {
10313       return _state;
10314     }
10315   };
10316 }
10317
10318 //================================================================================
10319 /*!
10320   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10321   This method is the first step of DoubleNodeElemGroupsInRegion.
10322   \param theElems - list of groups of elements (edges or faces) to be replicated
10323   \param theNodesNot - list of groups of nodes not to replicated
10324   \param theShape - shape to detect affected elements (element which geometric center
10325          located on or inside shape). If the shape is null, detection is done on faces orientations
10326          (select elements with a gravity center on the side given by faces normals).
10327          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10328          The replicated nodes should be associated to affected elements.
10329   \return groups of affected elements
10330   \sa DoubleNodeElemGroupsInRegion()
10331  */
10332 //================================================================================
10333
10334 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10335                                                    const TIDSortedElemSet& theNodesNot,
10336                                                    const TopoDS_Shape&     theShape,
10337                                                    TIDSortedElemSet&       theAffectedElems)
10338 {
10339   if ( theShape.IsNull() )
10340   {
10341     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10342     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10343     std::set<const SMDS_MeshElement*> edgesToCheck;
10344     alreadyCheckedNodes.clear();
10345     alreadyCheckedElems.clear();
10346     edgesToCheck.clear();
10347
10348     // --- iterates on elements to be replicated and get elements by back references from their nodes
10349
10350     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10351     int ielem;
10352     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10353     {
10354       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10355       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10356         continue;
10357       gp_XYZ normal;
10358       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10359       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10360       std::set<const SMDS_MeshNode*> nodesElem;
10361       nodesElem.clear();
10362       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10363       while ( nodeItr->more() )
10364       {
10365         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10366         nodesElem.insert(aNode);
10367       }
10368       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10369       for (; nodit != nodesElem.end(); nodit++)
10370       {
10371         MESSAGE("  noeud ");
10372         const SMDS_MeshNode* aNode = *nodit;
10373         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10374           continue;
10375         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10376           continue;
10377         alreadyCheckedNodes.insert(aNode);
10378         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10379         while ( backElemItr->more() )
10380         {
10381           MESSAGE("    backelem ");
10382           const SMDS_MeshElement* curElem = backElemItr->next();
10383           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10384             continue;
10385           if (theElems.find(curElem) != theElems.end())
10386             continue;
10387           alreadyCheckedElems.insert(curElem);
10388           double x=0, y=0, z=0;
10389           int nb = 0;
10390           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10391           while ( nodeItr2->more() )
10392           {
10393             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10394             x += anotherNode->X();
10395             y += anotherNode->Y();
10396             z += anotherNode->Z();
10397             nb++;
10398           }
10399           gp_XYZ p;
10400           p.SetCoord( x/nb -aNode->X(),
10401                       y/nb -aNode->Y(),
10402                       z/nb -aNode->Z() );
10403           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10404           if (normal*p > 0)
10405           {
10406             MESSAGE("    --- inserted")
10407             theAffectedElems.insert( curElem );
10408           }
10409           else if (curElem->GetType() == SMDSAbs_Edge)
10410             edgesToCheck.insert(curElem);
10411         }
10412       }
10413     }
10414     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10415     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10416     for( ; eit != edgesToCheck.end(); eit++)
10417     {
10418       bool onside = true;
10419       const SMDS_MeshElement* anEdge = *eit;
10420       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10421       while ( nodeItr->more() )
10422       {
10423         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10424         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10425         {
10426           onside = false;
10427           break;
10428         }
10429       }
10430       if (onside)
10431       {
10432         MESSAGE("    --- edge onside inserted")
10433         theAffectedElems.insert(anEdge);
10434       }
10435     }
10436   }
10437   else
10438   {
10439     const double aTol = Precision::Confusion();
10440     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10441     auto_ptr<_FaceClassifier>              aFaceClassifier;
10442     if ( theShape.ShapeType() == TopAbs_SOLID )
10443     {
10444       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10445       bsc3d->PerformInfinitePoint(aTol);
10446     }
10447     else if (theShape.ShapeType() == TopAbs_FACE )
10448     {
10449       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10450     }
10451
10452     // iterates on indicated elements and get elements by back references from their nodes
10453     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10454     int ielem;
10455     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10456     {
10457       MESSAGE("element " << ielem++);
10458       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10459       if (!anElem)
10460         continue;
10461       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10462       while ( nodeItr->more() )
10463       {
10464         MESSAGE("  noeud ");
10465         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10466         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10467           continue;
10468         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10469         while ( backElemItr->more() )
10470         {
10471           MESSAGE("    backelem ");
10472           const SMDS_MeshElement* curElem = backElemItr->next();
10473           if ( curElem && theElems.find(curElem) == theElems.end() &&
10474               ( bsc3d.get() ?
10475                 isInside( curElem, *bsc3d, aTol ) :
10476                 isInside( curElem, *aFaceClassifier, aTol )))
10477             theAffectedElems.insert( curElem );
10478         }
10479       }
10480     }
10481   }
10482   return true;
10483 }
10484
10485 //================================================================================
10486 /*!
10487   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10488   \param theElems - group of of elements (edges or faces) to be replicated
10489   \param theNodesNot - group of nodes not to replicate
10490   \param theShape - shape to detect affected elements (element which geometric center
10491   located on or inside shape).
10492   The replicated nodes should be associated to affected elements.
10493   \return TRUE if operation has been completed successfully, FALSE otherwise
10494 */
10495 //================================================================================
10496
10497 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10498                                             const TIDSortedElemSet& theNodesNot,
10499                                             const TopoDS_Shape&     theShape )
10500 {
10501   if ( theShape.IsNull() )
10502     return false;
10503
10504   const double aTol = Precision::Confusion();
10505   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10506   auto_ptr<_FaceClassifier>              aFaceClassifier;
10507   if ( theShape.ShapeType() == TopAbs_SOLID )
10508   {
10509     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10510     bsc3d->PerformInfinitePoint(aTol);
10511   }
10512   else if (theShape.ShapeType() == TopAbs_FACE )
10513   {
10514     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10515   }
10516
10517   // iterates on indicated elements and get elements by back references from their nodes
10518   TIDSortedElemSet anAffected;
10519   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10520   for ( ;  elemItr != theElems.end(); ++elemItr )
10521   {
10522     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10523     if (!anElem)
10524       continue;
10525
10526     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10527     while ( nodeItr->more() )
10528     {
10529       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10530       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10531         continue;
10532       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10533       while ( backElemItr->more() )
10534       {
10535         const SMDS_MeshElement* curElem = backElemItr->next();
10536         if ( curElem && theElems.find(curElem) == theElems.end() &&
10537              ( bsc3d.get() ?
10538                isInside( curElem, *bsc3d, aTol ) :
10539                isInside( curElem, *aFaceClassifier, aTol )))
10540           anAffected.insert( curElem );
10541       }
10542     }
10543   }
10544   return DoubleNodes( theElems, theNodesNot, anAffected );
10545 }
10546
10547 /*!
10548  *  \brief compute an oriented angle between two planes defined by four points.
10549  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10550  *  @param p0 base of the rotation axe
10551  *  @param p1 extremity of the rotation axe
10552  *  @param g1 belongs to the first plane
10553  *  @param g2 belongs to the second plane
10554  */
10555 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10556 {
10557 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10558 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10559 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10560 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10561   gp_Vec vref(p0, p1);
10562   gp_Vec v1(p0, g1);
10563   gp_Vec v2(p0, g2);
10564   gp_Vec n1 = vref.Crossed(v1);
10565   gp_Vec n2 = vref.Crossed(v2);
10566   try {
10567     return n2.AngleWithRef(n1, vref);
10568   }
10569   catch ( Standard_Failure ) {
10570   }
10571   return Max( v1.Magnitude(), v2.Magnitude() );
10572 }
10573
10574 /*!
10575  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10576  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10577  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10578  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10579  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10580  * 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.
10581  * 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.
10582  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10583  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10584  * \param theElems - list of groups of volumes, where a group of volume is a set of
10585  *        SMDS_MeshElements sorted by Id.
10586  * \param createJointElems - if TRUE, create the elements
10587  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10588  *        the boundary between \a theDomains and the rest mesh
10589  * \return TRUE if operation has been completed successfully, FALSE otherwise
10590  */
10591 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10592                                                      bool                                 createJointElems,
10593                                                      bool                                 onAllBoundaries)
10594 {
10595   MESSAGE("----------------------------------------------");
10596   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10597   MESSAGE("----------------------------------------------");
10598
10599   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10600   meshDS->BuildDownWardConnectivity(true);
10601   CHRONO(50);
10602   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10603
10604   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10605   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10606   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10607
10608   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10609   std::map<int,int>celldom; // cell vtkId --> domain
10610   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10611   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10612   faceDomains.clear();
10613   celldom.clear();
10614   cellDomains.clear();
10615   nodeDomains.clear();
10616   std::map<int,int> emptyMap;
10617   std::set<int> emptySet;
10618   emptyMap.clear();
10619
10620   MESSAGE(".. Number of domains :"<<theElems.size());
10621
10622   TIDSortedElemSet theRestDomElems;
10623   const int iRestDom  = -1;
10624   const int idom0     = onAllBoundaries ? iRestDom : 0;
10625   const int nbDomains = theElems.size();
10626
10627   // Check if the domains do not share an element
10628   for (int idom = 0; idom < nbDomains-1; idom++)
10629     {
10630 //       MESSAGE("... Check of domain #" << idom);
10631       const TIDSortedElemSet& domain = theElems[idom];
10632       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10633       for (; elemItr != domain.end(); ++elemItr)
10634         {
10635           const SMDS_MeshElement* anElem = *elemItr;
10636           int idombisdeb = idom + 1 ;
10637           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10638           {
10639             const TIDSortedElemSet& domainbis = theElems[idombis];
10640             if ( domainbis.count(anElem) )
10641             {
10642               MESSAGE(".... Domain #" << idom);
10643               MESSAGE(".... Domain #" << idombis);
10644               throw SALOME_Exception("The domains are not disjoint.");
10645               return false ;
10646             }
10647           }
10648         }
10649     }
10650
10651   for (int idom = 0; idom < nbDomains; idom++)
10652     {
10653
10654       // --- build a map (face to duplicate --> volume to modify)
10655       //     with all the faces shared by 2 domains (group of elements)
10656       //     and corresponding volume of this domain, for each shared face.
10657       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10658
10659       MESSAGE("... Neighbors of domain #" << idom);
10660       const TIDSortedElemSet& domain = theElems[idom];
10661       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10662       for (; elemItr != domain.end(); ++elemItr)
10663         {
10664           const SMDS_MeshElement* anElem = *elemItr;
10665           if (!anElem)
10666             continue;
10667           int vtkId = anElem->getVtkId();
10668           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10669           int neighborsVtkIds[NBMAXNEIGHBORS];
10670           int downIds[NBMAXNEIGHBORS];
10671           unsigned char downTypes[NBMAXNEIGHBORS];
10672           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10673           for (int n = 0; n < nbNeighbors; n++)
10674             {
10675               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10676               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10677               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10678                 {
10679                   bool ok = false ;
10680                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10681                   {
10682                     // MESSAGE("Domain " << idombis);
10683                     const TIDSortedElemSet& domainbis = theElems[idombis];
10684                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10685                   }
10686                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10687                   {
10688                     DownIdType face(downIds[n], downTypes[n]);
10689                     if (!faceDomains[face].count(idom))
10690                       {
10691                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10692                         celldom[vtkId] = idom;
10693                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10694                       }
10695                     if ( !ok )
10696                     {
10697                       theRestDomElems.insert( elem );
10698                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10699                       celldom[neighborsVtkIds[n]] = iRestDom;
10700                     }
10701                   }
10702                 }
10703             }
10704         }
10705     }
10706
10707   //MESSAGE("Number of shared faces " << faceDomains.size());
10708   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10709
10710   // --- explore the shared faces domain by domain,
10711   //     explore the nodes of the face and see if they belong to a cell in the domain,
10712   //     which has only a node or an edge on the border (not a shared face)
10713
10714   for (int idomain = idom0; idomain < nbDomains; idomain++)
10715     {
10716       //MESSAGE("Domain " << idomain);
10717       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10718       itface = faceDomains.begin();
10719       for (; itface != faceDomains.end(); ++itface)
10720         {
10721           const std::map<int, int>& domvol = itface->second;
10722           if (!domvol.count(idomain))
10723             continue;
10724           DownIdType face = itface->first;
10725           //MESSAGE(" --- face " << face.cellId);
10726           std::set<int> oldNodes;
10727           oldNodes.clear();
10728           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10729           std::set<int>::iterator itn = oldNodes.begin();
10730           for (; itn != oldNodes.end(); ++itn)
10731             {
10732               int oldId = *itn;
10733               //MESSAGE("     node " << oldId);
10734               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10735               for (int i=0; i<l.ncells; i++)
10736                 {
10737                   int vtkId = l.cells[i];
10738                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10739                   if (!domain.count(anElem))
10740                     continue;
10741                   int vtkType = grid->GetCellType(vtkId);
10742                   int downId = grid->CellIdToDownId(vtkId);
10743                   if (downId < 0)
10744                     {
10745                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10746                       continue; // not OK at this stage of the algorithm:
10747                                 //no cells created after BuildDownWardConnectivity
10748                     }
10749                   DownIdType aCell(downId, vtkType);
10750                   cellDomains[aCell][idomain] = vtkId;
10751                   celldom[vtkId] = idomain;
10752                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10753                 }
10754             }
10755         }
10756     }
10757
10758   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10759   //     for each shared face, get the nodes
10760   //     for each node, for each domain of the face, create a clone of the node
10761
10762   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10763   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10764   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10765
10766   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10767   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10768   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10769
10770   MESSAGE(".. Duplication of the nodes");
10771   for (int idomain = idom0; idomain < nbDomains; idomain++)
10772     {
10773       itface = faceDomains.begin();
10774       for (; itface != faceDomains.end(); ++itface)
10775         {
10776           const std::map<int, int>& domvol = itface->second;
10777           if (!domvol.count(idomain))
10778             continue;
10779           DownIdType face = itface->first;
10780           //MESSAGE(" --- face " << face.cellId);
10781           std::set<int> oldNodes;
10782           oldNodes.clear();
10783           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10784           std::set<int>::iterator itn = oldNodes.begin();
10785           for (; itn != oldNodes.end(); ++itn)
10786             {
10787               int oldId = *itn;
10788               if (nodeDomains[oldId].empty())
10789                 {
10790                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10791                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10792                 }
10793               std::map<int, int>::const_iterator itdom = domvol.begin();
10794               for (; itdom != domvol.end(); ++itdom)
10795                 {
10796                   int idom = itdom->first;
10797                   //MESSAGE("         domain " << idom);
10798                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10799                     {
10800                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10801                         {
10802                           vector<int> orderedDoms;
10803                           //MESSAGE("multiple node " << oldId);
10804                           if (mutipleNodes.count(oldId))
10805                             orderedDoms = mutipleNodes[oldId];
10806                           else
10807                             {
10808                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10809                               for (; it != nodeDomains[oldId].end(); ++it)
10810                                 orderedDoms.push_back(it->first);
10811                             }
10812                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10813                           //stringstream txt;
10814                           //for (int i=0; i<orderedDoms.size(); i++)
10815                           //  txt << orderedDoms[i] << " ";
10816                           //MESSAGE("orderedDoms " << txt.str());
10817                           mutipleNodes[oldId] = orderedDoms;
10818                         }
10819                       double *coords = grid->GetPoint(oldId);
10820                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10821                       int newId = newNode->getVtkId();
10822                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10823                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10824                     }
10825                 }
10826             }
10827         }
10828     }
10829
10830   MESSAGE(".. Creation of elements");
10831   for (int idomain = idom0; idomain < nbDomains; idomain++)
10832     {
10833       itface = faceDomains.begin();
10834       for (; itface != faceDomains.end(); ++itface)
10835         {
10836           std::map<int, int> domvol = itface->second;
10837           if (!domvol.count(idomain))
10838             continue;
10839           DownIdType face = itface->first;
10840           //MESSAGE(" --- face " << face.cellId);
10841           std::set<int> oldNodes;
10842           oldNodes.clear();
10843           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10844           int nbMultipleNodes = 0;
10845           std::set<int>::iterator itn = oldNodes.begin();
10846           for (; itn != oldNodes.end(); ++itn)
10847             {
10848               int oldId = *itn;
10849               if (mutipleNodes.count(oldId))
10850                 nbMultipleNodes++;
10851             }
10852           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10853             {
10854               //MESSAGE("multiple Nodes detected on a shared face");
10855               int downId = itface->first.cellId;
10856               unsigned char cellType = itface->first.cellType;
10857               // --- shared edge or shared face ?
10858               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10859                 {
10860                   int nodes[3];
10861                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10862                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10863                     if (mutipleNodes.count(nodes[i]))
10864                       if (!mutipleNodesToFace.count(nodes[i]))
10865                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10866                 }
10867               else // shared face (between two volumes)
10868                 {
10869                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10870                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10871                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10872                   for (int ie =0; ie < nbEdges; ie++)
10873                     {
10874                       int nodes[3];
10875                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10876                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10877                         {
10878                           vector<int> vn0 = mutipleNodes[nodes[0]];
10879                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10880                           vector<int> doms;
10881                           for (int i0 = 0; i0 < vn0.size(); i0++)
10882                             for (int i1 = 0; i1 < vn1.size(); i1++)
10883                               if (vn0[i0] == vn1[i1])
10884                                 doms.push_back(vn0[i0]);
10885                           if (doms.size() >2)
10886                             {
10887                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10888                               double *coords = grid->GetPoint(nodes[0]);
10889                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10890                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10891                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10892                               gp_Pnt gref;
10893                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10894                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10895                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10896                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10897                               for (int id=0; id < doms.size(); id++)
10898                                 {
10899                                   int idom = doms[id];
10900                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10901                                   for (int ivol=0; ivol<nbvol; ivol++)
10902                                     {
10903                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10904                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10905                                       if (domain.count(elem))
10906                                         {
10907                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10908                                           domvol[idom] = svol;
10909                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10910                                           double values[3];
10911                                           vtkIdType npts = 0;
10912                                           vtkIdType* pts = 0;
10913                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10914                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10915                                           if (id ==0)
10916                                             {
10917                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10918                                               angleDom[idom] = 0;
10919                                             }
10920                                           else
10921                                             {
10922                                               gp_Pnt g(values[0], values[1], values[2]);
10923                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10924                                               //MESSAGE("  angle=" << angleDom[idom]);
10925                                             }
10926                                           break;
10927                                         }
10928                                     }
10929                                 }
10930                               map<double, int> sortedDom; // sort domains by angle
10931                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10932                                 sortedDom[ia->second] = ia->first;
10933                               vector<int> vnodes;
10934                               vector<int> vdom;
10935                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10936                                 {
10937                                   vdom.push_back(ib->second);
10938                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10939                                 }
10940                               for (int ino = 0; ino < nbNodes; ino++)
10941                                 vnodes.push_back(nodes[ino]);
10942                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10943                             }
10944                         }
10945                     }
10946                 }
10947             }
10948         }
10949     }
10950
10951   // --- iterate on shared faces (volumes to modify, face to extrude)
10952   //     get node id's of the face (id SMDS = id VTK)
10953   //     create flat element with old and new nodes if requested
10954
10955   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10956   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10957
10958   std::map<int, std::map<long,int> > nodeQuadDomains;
10959   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10960
10961   MESSAGE(".. Creation of elements: simple junction");
10962   if (createJointElems)
10963     {
10964       int idg;
10965       string joints2DName = "joints2D";
10966       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10967       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10968       string joints3DName = "joints3D";
10969       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10970       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10971
10972       itface = faceDomains.begin();
10973       for (; itface != faceDomains.end(); ++itface)
10974         {
10975           DownIdType face = itface->first;
10976           std::set<int> oldNodes;
10977           std::set<int>::iterator itn;
10978           oldNodes.clear();
10979           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10980
10981           std::map<int, int> domvol = itface->second;
10982           std::map<int, int>::iterator itdom = domvol.begin();
10983           int dom1 = itdom->first;
10984           int vtkVolId = itdom->second;
10985           itdom++;
10986           int dom2 = itdom->first;
10987           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10988                                                              nodeQuadDomains);
10989           stringstream grpname;
10990           grpname << "j_";
10991           if (dom1 < dom2)
10992             grpname << dom1 << "_" << dom2;
10993           else
10994             grpname << dom2 << "_" << dom1;
10995           string namegrp = grpname.str();
10996           if (!mapOfJunctionGroups.count(namegrp))
10997             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10998           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10999           if (sgrp)
11000             sgrp->Add(vol->GetID());
11001           if (vol->GetType() == SMDSAbs_Volume)
11002             joints3DGrp->Add(vol->GetID());
11003           else if (vol->GetType() == SMDSAbs_Face)
11004             joints2DGrp->Add(vol->GetID());
11005         }
11006     }
11007
11008   // --- create volumes on multiple domain intersection if requested
11009   //     iterate on mutipleNodesToFace
11010   //     iterate on edgesMultiDomains
11011
11012   MESSAGE(".. Creation of elements: multiple junction");
11013   if (createJointElems)
11014     {
11015       // --- iterate on mutipleNodesToFace
11016
11017       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11018       for (; itn != mutipleNodesToFace.end(); ++itn)
11019         {
11020           int node = itn->first;
11021           vector<int> orderDom = itn->second;
11022           vector<vtkIdType> orderedNodes;
11023           for (int idom = 0; idom <orderDom.size(); idom++)
11024             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11025             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11026
11027             stringstream grpname;
11028             grpname << "m2j_";
11029             grpname << 0 << "_" << 0;
11030             int idg;
11031             string namegrp = grpname.str();
11032             if (!mapOfJunctionGroups.count(namegrp))
11033               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11034             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11035             if (sgrp)
11036               sgrp->Add(face->GetID());
11037         }
11038
11039       // --- iterate on edgesMultiDomains
11040
11041       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11042       for (; ite != edgesMultiDomains.end(); ++ite)
11043         {
11044           vector<int> nodes = ite->first;
11045           vector<int> orderDom = ite->second;
11046           vector<vtkIdType> orderedNodes;
11047           if (nodes.size() == 2)
11048             {
11049               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11050               for (int ino=0; ino < nodes.size(); ino++)
11051                 if (orderDom.size() == 3)
11052                   for (int idom = 0; idom <orderDom.size(); idom++)
11053                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11054                 else
11055                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11056                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11057               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11058
11059               int idg;
11060               string namegrp = "jointsMultiples";
11061               if (!mapOfJunctionGroups.count(namegrp))
11062                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11063               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11064               if (sgrp)
11065                 sgrp->Add(vol->GetID());
11066             }
11067           else
11068             {
11069               INFOS("Quadratic multiple joints not implemented");
11070               // TODO quadratic nodes
11071             }
11072         }
11073     }
11074
11075   // --- list the explicit faces and edges of the mesh that need to be modified,
11076   //     i.e. faces and edges built with one or more duplicated nodes.
11077   //     associate these faces or edges to their corresponding domain.
11078   //     only the first domain found is kept when a face or edge is shared
11079
11080   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11081   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11082   faceOrEdgeDom.clear();
11083   feDom.clear();
11084
11085   MESSAGE(".. Modification of elements");
11086   for (int idomain = idom0; idomain < nbDomains; idomain++)
11087     {
11088       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11089       for (; itnod != nodeDomains.end(); ++itnod)
11090         {
11091           int oldId = itnod->first;
11092           //MESSAGE("     node " << oldId);
11093           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094           for (int i = 0; i < l.ncells; i++)
11095             {
11096               int vtkId = l.cells[i];
11097               int vtkType = grid->GetCellType(vtkId);
11098               int downId = grid->CellIdToDownId(vtkId);
11099               if (downId < 0)
11100                 continue; // new cells: not to be modified
11101               DownIdType aCell(downId, vtkType);
11102               int volParents[1000];
11103               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11104               for (int j = 0; j < nbvol; j++)
11105                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11106                   if (!feDom.count(vtkId))
11107                     {
11108                       feDom[vtkId] = idomain;
11109                       faceOrEdgeDom[aCell] = emptyMap;
11110                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11111                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11112                       //        << " type " << vtkType << " downId " << downId);
11113                     }
11114             }
11115         }
11116     }
11117
11118   // --- iterate on shared faces (volumes to modify, face to extrude)
11119   //     get node id's of the face
11120   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11121
11122   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11123   for (int m=0; m<3; m++)
11124     {
11125       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11126       itface = (*amap).begin();
11127       for (; itface != (*amap).end(); ++itface)
11128         {
11129           DownIdType face = itface->first;
11130           std::set<int> oldNodes;
11131           std::set<int>::iterator itn;
11132           oldNodes.clear();
11133           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11134           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11135           std::map<int, int> localClonedNodeIds;
11136
11137           std::map<int, int> domvol = itface->second;
11138           std::map<int, int>::iterator itdom = domvol.begin();
11139           for (; itdom != domvol.end(); ++itdom)
11140             {
11141               int idom = itdom->first;
11142               int vtkVolId = itdom->second;
11143               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11144               localClonedNodeIds.clear();
11145               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11146                 {
11147                   int oldId = *itn;
11148                   if (nodeDomains[oldId].count(idom))
11149                     {
11150                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11151                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11152                     }
11153                 }
11154               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11155             }
11156         }
11157     }
11158
11159   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11160   grid->BuildLinks();
11161
11162   CHRONOSTOP(50);
11163   counters::stats();
11164   return true;
11165 }
11166
11167 /*!
11168  * \brief Double nodes on some external faces and create flat elements.
11169  * Flat elements are mainly used by some types of mechanic calculations.
11170  *
11171  * Each group of the list must be constituted of faces.
11172  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11173  * @param theElems - list of groups of faces, where a group of faces is a set of
11174  * SMDS_MeshElements sorted by Id.
11175  * @return TRUE if operation has been completed successfully, FALSE otherwise
11176  */
11177 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11178 {
11179   MESSAGE("-------------------------------------------------");
11180   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11181   MESSAGE("-------------------------------------------------");
11182
11183   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11184
11185   // --- For each group of faces
11186   //     duplicate the nodes, create a flat element based on the face
11187   //     replace the nodes of the faces by their clones
11188
11189   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11190   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11191   clonedNodes.clear();
11192   intermediateNodes.clear();
11193   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11194   mapOfJunctionGroups.clear();
11195
11196   for (int idom = 0; idom < theElems.size(); idom++)
11197     {
11198       const TIDSortedElemSet& domain = theElems[idom];
11199       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11200       for (; elemItr != domain.end(); ++elemItr)
11201         {
11202           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11203           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11204           if (!aFace)
11205             continue;
11206           // MESSAGE("aFace=" << aFace->GetID());
11207           bool isQuad = aFace->IsQuadratic();
11208           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11209
11210           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11211
11212           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11213           while (nodeIt->more())
11214             {
11215               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11216               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11217               if (isMedium)
11218                 ln2.push_back(node);
11219               else
11220                 ln0.push_back(node);
11221
11222               const SMDS_MeshNode* clone = 0;
11223               if (!clonedNodes.count(node))
11224                 {
11225                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11226                   clonedNodes[node] = clone;
11227                 }
11228               else
11229                 clone = clonedNodes[node];
11230
11231               if (isMedium)
11232                 ln3.push_back(clone);
11233               else
11234                 ln1.push_back(clone);
11235
11236               const SMDS_MeshNode* inter = 0;
11237               if (isQuad && (!isMedium))
11238                 {
11239                   if (!intermediateNodes.count(node))
11240                     {
11241                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11242                       intermediateNodes[node] = inter;
11243                     }
11244                   else
11245                     inter = intermediateNodes[node];
11246                   ln4.push_back(inter);
11247                 }
11248             }
11249
11250           // --- extrude the face
11251
11252           vector<const SMDS_MeshNode*> ln;
11253           SMDS_MeshVolume* vol = 0;
11254           vtkIdType aType = aFace->GetVtkType();
11255           switch (aType)
11256           {
11257             case VTK_TRIANGLE:
11258               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11259               // MESSAGE("vol prism " << vol->GetID());
11260               ln.push_back(ln1[0]);
11261               ln.push_back(ln1[1]);
11262               ln.push_back(ln1[2]);
11263               break;
11264             case VTK_QUAD:
11265               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11266               // MESSAGE("vol hexa " << vol->GetID());
11267               ln.push_back(ln1[0]);
11268               ln.push_back(ln1[1]);
11269               ln.push_back(ln1[2]);
11270               ln.push_back(ln1[3]);
11271               break;
11272             case VTK_QUADRATIC_TRIANGLE:
11273               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11274                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11275               // MESSAGE("vol quad prism " << vol->GetID());
11276               ln.push_back(ln1[0]);
11277               ln.push_back(ln1[1]);
11278               ln.push_back(ln1[2]);
11279               ln.push_back(ln3[0]);
11280               ln.push_back(ln3[1]);
11281               ln.push_back(ln3[2]);
11282               break;
11283             case VTK_QUADRATIC_QUAD:
11284 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11285 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11286 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11287               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11288                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11289                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11290               // MESSAGE("vol quad hexa " << vol->GetID());
11291               ln.push_back(ln1[0]);
11292               ln.push_back(ln1[1]);
11293               ln.push_back(ln1[2]);
11294               ln.push_back(ln1[3]);
11295               ln.push_back(ln3[0]);
11296               ln.push_back(ln3[1]);
11297               ln.push_back(ln3[2]);
11298               ln.push_back(ln3[3]);
11299               break;
11300             case VTK_POLYGON:
11301               break;
11302             default:
11303               break;
11304           }
11305
11306           if (vol)
11307             {
11308               stringstream grpname;
11309               grpname << "jf_";
11310               grpname << idom;
11311               int idg;
11312               string namegrp = grpname.str();
11313               if (!mapOfJunctionGroups.count(namegrp))
11314                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11315               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11316               if (sgrp)
11317                 sgrp->Add(vol->GetID());
11318             }
11319
11320           // --- modify the face
11321
11322           aFace->ChangeNodes(&ln[0], ln.size());
11323         }
11324     }
11325   return true;
11326 }
11327
11328 /*!
11329  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11330  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11331  *  groups of faces to remove inside the object, (idem edges).
11332  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11333  */
11334 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11335                                       const TopoDS_Shape& theShape,
11336                                       SMESH_NodeSearcher* theNodeSearcher,
11337                                       const char* groupName,
11338                                       std::vector<double>&   nodesCoords,
11339                                       std::vector<std::vector<int> >& listOfListOfNodes)
11340 {
11341   MESSAGE("--------------------------------");
11342   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11343   MESSAGE("--------------------------------");
11344
11345   // --- zone of volumes to remove is given :
11346   //     1 either by a geom shape (one or more vertices) and a radius,
11347   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11348   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11349   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11350   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11351   //     defined by it's name.
11352
11353   SMESHDS_GroupBase* groupDS = 0;
11354   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11355   while ( groupIt->more() )
11356     {
11357       groupDS = 0;
11358       SMESH_Group * group = groupIt->next();
11359       if ( !group ) continue;
11360       groupDS = group->GetGroupDS();
11361       if ( !groupDS || groupDS->IsEmpty() ) continue;
11362       std::string grpName = group->GetName();
11363       //MESSAGE("grpName=" << grpName);
11364       if (grpName == groupName)
11365         break;
11366       else
11367         groupDS = 0;
11368     }
11369
11370   bool isNodeGroup = false;
11371   bool isNodeCoords = false;
11372   if (groupDS)
11373     {
11374       if (groupDS->GetType() != SMDSAbs_Node)
11375         return;
11376       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11377     }
11378
11379   if (nodesCoords.size() > 0)
11380     isNodeCoords = true; // a list o nodes given by their coordinates
11381   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11382
11383   // --- define groups to build
11384
11385   int idg; // --- group of SMDS volumes
11386   string grpvName = groupName;
11387   grpvName += "_vol";
11388   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11389   if (!grp)
11390     {
11391       MESSAGE("group not created " << grpvName);
11392       return;
11393     }
11394   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11395
11396   int idgs; // --- group of SMDS faces on the skin
11397   string grpsName = groupName;
11398   grpsName += "_skin";
11399   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11400   if (!grps)
11401     {
11402       MESSAGE("group not created " << grpsName);
11403       return;
11404     }
11405   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11406
11407   int idgi; // --- group of SMDS faces internal (several shapes)
11408   string grpiName = groupName;
11409   grpiName += "_internalFaces";
11410   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11411   if (!grpi)
11412     {
11413       MESSAGE("group not created " << grpiName);
11414       return;
11415     }
11416   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11417
11418   int idgei; // --- group of SMDS faces internal (several shapes)
11419   string grpeiName = groupName;
11420   grpeiName += "_internalEdges";
11421   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11422   if (!grpei)
11423     {
11424       MESSAGE("group not created " << grpeiName);
11425       return;
11426     }
11427   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11428
11429   // --- build downward connectivity
11430
11431   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11432   meshDS->BuildDownWardConnectivity(true);
11433   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11434
11435   // --- set of volumes detected inside
11436
11437   std::set<int> setOfInsideVol;
11438   std::set<int> setOfVolToCheck;
11439
11440   std::vector<gp_Pnt> gpnts;
11441   gpnts.clear();
11442
11443   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11444     {
11445       MESSAGE("group of nodes provided");
11446       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11447       while ( elemIt->more() )
11448         {
11449           const SMDS_MeshElement* elem = elemIt->next();
11450           if (!elem)
11451             continue;
11452           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11453           if (!node)
11454             continue;
11455           SMDS_MeshElement* vol = 0;
11456           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11457           while (volItr->more())
11458             {
11459               vol = (SMDS_MeshElement*)volItr->next();
11460               setOfInsideVol.insert(vol->getVtkId());
11461               sgrp->Add(vol->GetID());
11462             }
11463         }
11464     }
11465   else if (isNodeCoords)
11466     {
11467       MESSAGE("list of nodes coordinates provided");
11468       int i = 0;
11469       int k = 0;
11470       while (i < nodesCoords.size()-2)
11471         {
11472           double x = nodesCoords[i++];
11473           double y = nodesCoords[i++];
11474           double z = nodesCoords[i++];
11475           gp_Pnt p = gp_Pnt(x, y ,z);
11476           gpnts.push_back(p);
11477           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11478           k++;
11479         }
11480     }
11481   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11482     {
11483       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11484       TopTools_IndexedMapOfShape vertexMap;
11485       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11486       gp_Pnt p = gp_Pnt(0,0,0);
11487       if (vertexMap.Extent() < 1)
11488         return;
11489
11490       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11491         {
11492           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11493           p = BRep_Tool::Pnt(vertex);
11494           gpnts.push_back(p);
11495           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11496         }
11497     }
11498
11499   if (gpnts.size() > 0)
11500     {
11501       int nodeId = 0;
11502       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11503       if (startNode)
11504         nodeId = startNode->GetID();
11505       MESSAGE("nodeId " << nodeId);
11506
11507       double radius2 = radius*radius;
11508       MESSAGE("radius2 " << radius2);
11509
11510       // --- volumes on start node
11511
11512       setOfVolToCheck.clear();
11513       SMDS_MeshElement* startVol = 0;
11514       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11515       while (volItr->more())
11516         {
11517           startVol = (SMDS_MeshElement*)volItr->next();
11518           setOfVolToCheck.insert(startVol->getVtkId());
11519         }
11520       if (setOfVolToCheck.empty())
11521         {
11522           MESSAGE("No volumes found");
11523           return;
11524         }
11525
11526       // --- starting with central volumes then their neighbors, check if they are inside
11527       //     or outside the domain, until no more new neighbor volume is inside.
11528       //     Fill the group of inside volumes
11529
11530       std::map<int, double> mapOfNodeDistance2;
11531       mapOfNodeDistance2.clear();
11532       std::set<int> setOfOutsideVol;
11533       while (!setOfVolToCheck.empty())
11534         {
11535           std::set<int>::iterator it = setOfVolToCheck.begin();
11536           int vtkId = *it;
11537           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11538           bool volInside = false;
11539           vtkIdType npts = 0;
11540           vtkIdType* pts = 0;
11541           grid->GetCellPoints(vtkId, npts, pts);
11542           for (int i=0; i<npts; i++)
11543             {
11544               double distance2 = 0;
11545               if (mapOfNodeDistance2.count(pts[i]))
11546                 {
11547                   distance2 = mapOfNodeDistance2[pts[i]];
11548                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11549                 }
11550               else
11551                 {
11552                   double *coords = grid->GetPoint(pts[i]);
11553                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11554                   distance2 = 1.E40;
11555                   for (int j=0; j<gpnts.size(); j++)
11556                     {
11557                       double d2 = aPoint.SquareDistance(gpnts[j]);
11558                       if (d2 < distance2)
11559                         {
11560                           distance2 = d2;
11561                           if (distance2 < radius2)
11562                             break;
11563                         }
11564                     }
11565                   mapOfNodeDistance2[pts[i]] = distance2;
11566                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11567                 }
11568               if (distance2 < radius2)
11569                 {
11570                   volInside = true; // one or more nodes inside the domain
11571                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11572                   break;
11573                 }
11574             }
11575           if (volInside)
11576             {
11577               setOfInsideVol.insert(vtkId);
11578               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11579               int neighborsVtkIds[NBMAXNEIGHBORS];
11580               int downIds[NBMAXNEIGHBORS];
11581               unsigned char downTypes[NBMAXNEIGHBORS];
11582               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11583               for (int n = 0; n < nbNeighbors; n++)
11584                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11585                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11586             }
11587           else
11588             {
11589               setOfOutsideVol.insert(vtkId);
11590               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11591             }
11592           setOfVolToCheck.erase(vtkId);
11593         }
11594     }
11595
11596   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11597   //     If yes, add the volume to the inside set
11598
11599   bool addedInside = true;
11600   std::set<int> setOfVolToReCheck;
11601   while (addedInside)
11602     {
11603       MESSAGE(" --------------------------- re check");
11604       addedInside = false;
11605       std::set<int>::iterator itv = setOfInsideVol.begin();
11606       for (; itv != setOfInsideVol.end(); ++itv)
11607         {
11608           int vtkId = *itv;
11609           int neighborsVtkIds[NBMAXNEIGHBORS];
11610           int downIds[NBMAXNEIGHBORS];
11611           unsigned char downTypes[NBMAXNEIGHBORS];
11612           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11613           for (int n = 0; n < nbNeighbors; n++)
11614             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11615               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11616         }
11617       setOfVolToCheck = setOfVolToReCheck;
11618       setOfVolToReCheck.clear();
11619       while  (!setOfVolToCheck.empty())
11620         {
11621           std::set<int>::iterator it = setOfVolToCheck.begin();
11622           int vtkId = *it;
11623           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11624             {
11625               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11626               int countInside = 0;
11627               int neighborsVtkIds[NBMAXNEIGHBORS];
11628               int downIds[NBMAXNEIGHBORS];
11629               unsigned char downTypes[NBMAXNEIGHBORS];
11630               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11631               for (int n = 0; n < nbNeighbors; n++)
11632                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11633                   countInside++;
11634               MESSAGE("countInside " << countInside);
11635               if (countInside > 1)
11636                 {
11637                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11638                   setOfInsideVol.insert(vtkId);
11639                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11640                   addedInside = true;
11641                 }
11642               else
11643                 setOfVolToReCheck.insert(vtkId);
11644             }
11645           setOfVolToCheck.erase(vtkId);
11646         }
11647     }
11648
11649   // --- map of Downward faces at the boundary, inside the global volume
11650   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11651   //     fill group of SMDS faces inside the volume (when several volume shapes)
11652   //     fill group of SMDS faces on the skin of the global volume (if skin)
11653
11654   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11655   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11656   std::set<int>::iterator it = setOfInsideVol.begin();
11657   for (; it != setOfInsideVol.end(); ++it)
11658     {
11659       int vtkId = *it;
11660       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11661       int neighborsVtkIds[NBMAXNEIGHBORS];
11662       int downIds[NBMAXNEIGHBORS];
11663       unsigned char downTypes[NBMAXNEIGHBORS];
11664       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11665       for (int n = 0; n < nbNeighbors; n++)
11666         {
11667           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11668           if (neighborDim == 3)
11669             {
11670               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11671                 {
11672                   DownIdType face(downIds[n], downTypes[n]);
11673                   boundaryFaces[face] = vtkId;
11674                 }
11675               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11676               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11677               if (vtkFaceId >= 0)
11678                 {
11679                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11680                   // find also the smds edges on this face
11681                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11682                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11683                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11684                   for (int i = 0; i < nbEdges; i++)
11685                     {
11686                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11687                       if (vtkEdgeId >= 0)
11688                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11689                     }
11690                 }
11691             }
11692           else if (neighborDim == 2) // skin of the volume
11693             {
11694               DownIdType face(downIds[n], downTypes[n]);
11695               skinFaces[face] = vtkId;
11696               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11697               if (vtkFaceId >= 0)
11698                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11699             }
11700         }
11701     }
11702
11703   // --- identify the edges constituting the wire of each subshape on the skin
11704   //     define polylines with the nodes of edges, equivalent to wires
11705   //     project polylines on subshapes, and partition, to get geom faces
11706
11707   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11708   std::set<int> emptySet;
11709   emptySet.clear();
11710   std::set<int> shapeIds;
11711
11712   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11713   while (itelem->more())
11714     {
11715       const SMDS_MeshElement *elem = itelem->next();
11716       int shapeId = elem->getshapeId();
11717       int vtkId = elem->getVtkId();
11718       if (!shapeIdToVtkIdSet.count(shapeId))
11719         {
11720           shapeIdToVtkIdSet[shapeId] = emptySet;
11721           shapeIds.insert(shapeId);
11722         }
11723       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11724     }
11725
11726   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11727   std::set<DownIdType, DownIdCompare> emptyEdges;
11728   emptyEdges.clear();
11729
11730   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11731   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11732     {
11733       int shapeId = itShape->first;
11734       MESSAGE(" --- Shape ID --- "<< shapeId);
11735       shapeIdToEdges[shapeId] = emptyEdges;
11736
11737       std::vector<int> nodesEdges;
11738
11739       std::set<int>::iterator its = itShape->second.begin();
11740       for (; its != itShape->second.end(); ++its)
11741         {
11742           int vtkId = *its;
11743           MESSAGE("     " << vtkId);
11744           int neighborsVtkIds[NBMAXNEIGHBORS];
11745           int downIds[NBMAXNEIGHBORS];
11746           unsigned char downTypes[NBMAXNEIGHBORS];
11747           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11748           for (int n = 0; n < nbNeighbors; n++)
11749             {
11750               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11751                 continue;
11752               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11753               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11754               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11755                 {
11756                   DownIdType edge(downIds[n], downTypes[n]);
11757                   if (!shapeIdToEdges[shapeId].count(edge))
11758                     {
11759                       shapeIdToEdges[shapeId].insert(edge);
11760                       int vtkNodeId[3];
11761                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11762                       nodesEdges.push_back(vtkNodeId[0]);
11763                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11764                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11765                     }
11766                 }
11767             }
11768         }
11769
11770       std::list<int> order;
11771       order.clear();
11772       if (nodesEdges.size() > 0)
11773         {
11774           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11775           nodesEdges[0] = -1;
11776           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11777           nodesEdges[1] = -1; // do not reuse this edge
11778           bool found = true;
11779           while (found)
11780             {
11781               int nodeTofind = order.back(); // try first to push back
11782               int i = 0;
11783               for (i = 0; i<nodesEdges.size(); i++)
11784                 if (nodesEdges[i] == nodeTofind)
11785                   break;
11786               if (i == nodesEdges.size())
11787                 found = false; // no follower found on back
11788               else
11789                 {
11790                   if (i%2) // odd ==> use the previous one
11791                     if (nodesEdges[i-1] < 0)
11792                       found = false;
11793                     else
11794                       {
11795                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11796                         nodesEdges[i-1] = -1;
11797                       }
11798                   else // even ==> use the next one
11799                     if (nodesEdges[i+1] < 0)
11800                       found = false;
11801                     else
11802                       {
11803                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11804                         nodesEdges[i+1] = -1;
11805                       }
11806                 }
11807               if (found)
11808                 continue;
11809               // try to push front
11810               found = true;
11811               nodeTofind = order.front(); // try to push front
11812               for (i = 0; i<nodesEdges.size(); i++)
11813                 if (nodesEdges[i] == nodeTofind)
11814                   break;
11815               if (i == nodesEdges.size())
11816                 {
11817                   found = false; // no predecessor found on front
11818                   continue;
11819                 }
11820               if (i%2) // odd ==> use the previous one
11821                 if (nodesEdges[i-1] < 0)
11822                   found = false;
11823                 else
11824                   {
11825                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11826                     nodesEdges[i-1] = -1;
11827                   }
11828               else // even ==> use the next one
11829                 if (nodesEdges[i+1] < 0)
11830                   found = false;
11831                 else
11832                   {
11833                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11834                     nodesEdges[i+1] = -1;
11835                   }
11836             }
11837         }
11838
11839
11840       std::vector<int> nodes;
11841       nodes.push_back(shapeId);
11842       std::list<int>::iterator itl = order.begin();
11843       for (; itl != order.end(); itl++)
11844         {
11845           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11846           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11847         }
11848       listOfListOfNodes.push_back(nodes);
11849     }
11850
11851   //     partition geom faces with blocFissure
11852   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11853   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11854
11855   return;
11856 }
11857
11858
11859 //================================================================================
11860 /*!
11861  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11862  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11863  * \return TRUE if operation has been completed successfully, FALSE otherwise
11864  */
11865 //================================================================================
11866
11867 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11868 {
11869   // iterates on volume elements and detect all free faces on them
11870   SMESHDS_Mesh* aMesh = GetMeshDS();
11871   if (!aMesh)
11872     return false;
11873   //bool res = false;
11874   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11875   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11876   while(vIt->more())
11877   {
11878     const SMDS_MeshVolume* volume = vIt->next();
11879     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11880     vTool.SetExternalNormal();
11881     //const bool isPoly = volume->IsPoly();
11882     const int iQuad = volume->IsQuadratic();
11883     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11884     {
11885       if (!vTool.IsFreeFace(iface))
11886         continue;
11887       nbFree++;
11888       vector<const SMDS_MeshNode *> nodes;
11889       int nbFaceNodes = vTool.NbFaceNodes(iface);
11890       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11891       int inode = 0;
11892       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11893         nodes.push_back(faceNodes[inode]);
11894       if (iQuad) { // add medium nodes
11895         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11896           nodes.push_back(faceNodes[inode]);
11897         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11898           nodes.push_back(faceNodes[8]);
11899       }
11900       // add new face based on volume nodes
11901       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11902         nbExisted++;
11903         continue; // face already exsist
11904       }
11905       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11906       nbCreated++;
11907     }
11908   }
11909   return ( nbFree==(nbExisted+nbCreated) );
11910 }
11911
11912 namespace
11913 {
11914   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11915   {
11916     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11917       return n;
11918     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11919   }
11920 }
11921 //================================================================================
11922 /*!
11923  * \brief Creates missing boundary elements
11924  *  \param elements - elements whose boundary is to be checked
11925  *  \param dimension - defines type of boundary elements to create
11926  *  \param group - a group to store created boundary elements in
11927  *  \param targetMesh - a mesh to store created boundary elements in
11928  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11929  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11930  *                                boundary elements will be copied into the targetMesh
11931  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11932  *                                boundary elements will be added into the new group
11933  *  \param aroundElements - if true, elements will be created on boundary of given
11934  *                          elements else, on boundary of the whole mesh.
11935  * \return nb of added boundary elements
11936  */
11937 //================================================================================
11938
11939 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11940                                        Bnd_Dimension           dimension,
11941                                        SMESH_Group*            group/*=0*/,
11942                                        SMESH_Mesh*             targetMesh/*=0*/,
11943                                        bool                    toCopyElements/*=false*/,
11944                                        bool                    toCopyExistingBoundary/*=false*/,
11945                                        bool                    toAddExistingBondary/*= false*/,
11946                                        bool                    aroundElements/*= false*/)
11947 {
11948   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11949   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11950   // hope that all elements are of the same type, do not check them all
11951   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11952     throw SALOME_Exception(LOCALIZED("wrong element type"));
11953
11954   if ( !targetMesh )
11955     toCopyElements = toCopyExistingBoundary = false;
11956
11957   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11958   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11959   int nbAddedBnd = 0;
11960
11961   // editor adding present bnd elements and optionally holding elements to add to the group
11962   SMESH_MeshEditor* presentEditor;
11963   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11964   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11965
11966   SMESH_MesherHelper helper( *myMesh );
11967   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11968   SMDS_VolumeTool vTool;
11969   TIDSortedElemSet avoidSet;
11970   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11971   int inode;
11972
11973   typedef vector<const SMDS_MeshNode*> TConnectivity;
11974
11975   SMDS_ElemIteratorPtr eIt;
11976   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11977   else                  eIt = elemSetIterator( elements );
11978
11979   while (eIt->more())
11980   {
11981     const SMDS_MeshElement* elem = eIt->next();
11982     const int              iQuad = elem->IsQuadratic();
11983
11984     // ------------------------------------------------------------------------------------
11985     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11986     // ------------------------------------------------------------------------------------
11987     vector<const SMDS_MeshElement*> presentBndElems;
11988     vector<TConnectivity>           missingBndElems;
11989     TConnectivity nodes, elemNodes;
11990     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11991     {
11992       vTool.SetExternalNormal();
11993       const SMDS_MeshElement* otherVol = 0;
11994       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11995       {
11996         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11997              ( !aroundElements || elements.count( otherVol )))
11998           continue;
11999         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12000         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12001         if ( missType == SMDSAbs_Edge ) // boundary edges
12002         {
12003           nodes.resize( 2+iQuad );
12004           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12005           {
12006             for ( int j = 0; j < nodes.size(); ++j )
12007               nodes[j] =nn[i+j];
12008             if ( const SMDS_MeshElement* edge =
12009                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12010               presentBndElems.push_back( edge );
12011             else
12012               missingBndElems.push_back( nodes );
12013           }
12014         }
12015         else // boundary face
12016         {
12017           nodes.clear();
12018           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12019             nodes.push_back( nn[inode] ); // add corner nodes
12020           if (iQuad)
12021             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12022               nodes.push_back( nn[inode] ); // add medium nodes
12023           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12024           if ( iCenter > 0 )
12025             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12026
12027           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12028                                                                SMDSAbs_Face, /*noMedium=*/false ))
12029             presentBndElems.push_back( f );
12030           else
12031             missingBndElems.push_back( nodes );
12032
12033           if ( targetMesh != myMesh )
12034           {
12035             // add 1D elements on face boundary to be added to a new mesh
12036             const SMDS_MeshElement* edge;
12037             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12038             {
12039               if ( iQuad )
12040                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12041               else
12042                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12043               if ( edge && avoidSet.insert( edge ).second )
12044                 presentBndElems.push_back( edge );
12045             }
12046           }
12047         }
12048       }
12049     }
12050     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12051     {
12052       avoidSet.clear(), avoidSet.insert( elem );
12053       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12054                         SMDS_MeshElement::iterator() );
12055       elemNodes.push_back( elemNodes[0] );
12056       nodes.resize( 2 + iQuad );
12057       const int nbLinks = elem->NbCornerNodes();
12058       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12059       {
12060         nodes[0] = elemNodes[iN];
12061         nodes[1] = elemNodes[iN+1+iQuad];
12062         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12063           continue; // not free link
12064
12065         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12066         if ( const SMDS_MeshElement* edge =
12067              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12068           presentBndElems.push_back( edge );
12069         else
12070           missingBndElems.push_back( nodes );
12071       }
12072     }
12073
12074     // ---------------------------------
12075     // 2. Add missing boundary elements
12076     // ---------------------------------
12077     if ( targetMesh != myMesh )
12078       // instead of making a map of nodes in this mesh and targetMesh,
12079       // we create nodes with same IDs.
12080       for ( int i = 0; i < missingBndElems.size(); ++i )
12081       {
12082         TConnectivity& srcNodes = missingBndElems[i];
12083         TConnectivity  nodes( srcNodes.size() );
12084         for ( inode = 0; inode < nodes.size(); ++inode )
12085           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12086         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12087                                                                    missType,
12088                                                                    /*noMedium=*/false))
12089           continue;
12090         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12091         ++nbAddedBnd;
12092       }
12093     else
12094       for ( int i = 0; i < missingBndElems.size(); ++i )
12095       {
12096         TConnectivity& nodes = missingBndElems[i];
12097         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12098                                                                    missType,
12099                                                                    /*noMedium=*/false))
12100           continue;
12101         SMDS_MeshElement* elem =
12102           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12103         ++nbAddedBnd;
12104
12105         // try to set a new element to a shape
12106         if ( myMesh->HasShapeToMesh() )
12107         {
12108           bool ok = true;
12109           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12110           const int nbN = nodes.size() / (iQuad+1 );
12111           for ( inode = 0; inode < nbN && ok; ++inode )
12112           {
12113             pair<int, TopAbs_ShapeEnum> i_stype =
12114               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12115             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12116               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12117           }
12118           if ( ok && mediumShapes.size() > 1 )
12119           {
12120             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12121             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12122             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12123             {
12124               if (( ok = ( stype_i->first != stype_i_0.first )))
12125                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12126                                         aMesh->IndexToShape( stype_i_0.second ));
12127             }
12128           }
12129           if ( ok && mediumShapes.begin()->first == missShapeType )
12130             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12131         }
12132       }
12133
12134     // ----------------------------------
12135     // 3. Copy present boundary elements
12136     // ----------------------------------
12137     if ( toCopyExistingBoundary )
12138       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12139       {
12140         const SMDS_MeshElement* e = presentBndElems[i];
12141         TConnectivity nodes( e->NbNodes() );
12142         for ( inode = 0; inode < nodes.size(); ++inode )
12143           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12144         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12145       }
12146     else // store present elements to add them to a group
12147       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12148       {
12149         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12150       }
12151
12152   } // loop on given elements
12153
12154   // ---------------------------------------------
12155   // 4. Fill group with boundary elements
12156   // ---------------------------------------------
12157   if ( group )
12158   {
12159     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12160       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12161         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12162   }
12163   tgtEditor.myLastCreatedElems.Clear();
12164   tgtEditor2.myLastCreatedElems.Clear();
12165
12166   // -----------------------
12167   // 5. Copy given elements
12168   // -----------------------
12169   if ( toCopyElements && targetMesh != myMesh )
12170   {
12171     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12172     else                  eIt = elemSetIterator( elements );
12173     while (eIt->more())
12174     {
12175       const SMDS_MeshElement* elem = eIt->next();
12176       TConnectivity nodes( elem->NbNodes() );
12177       for ( inode = 0; inode < nodes.size(); ++inode )
12178         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12179       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12180
12181       tgtEditor.myLastCreatedElems.Clear();
12182     }
12183   }
12184   return nbAddedBnd;
12185 }