Salome HOME
IPAL52726: Hypotheses are missing from Object Browser after re-assignement
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  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 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140
141 //=======================================================================
142 /*!
143  * \brief Add element
144  */
145 //=======================================================================
146
147 SMDS_MeshElement*
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149                              const SMDSAbs_ElementType            type,
150                              const bool                           isPoly,
151                              const int                            ID,
152                              const double                         ballDiameter)
153 {
154   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155   SMDS_MeshElement* e = 0;
156   int nbnode = node.size();
157   SMESHDS_Mesh* mesh = GetMeshDS();
158   switch ( type ) {
159   case SMDSAbs_Face:
160     if ( !isPoly ) {
161       if      (nbnode == 3) {
162         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163         else           e = mesh->AddFace      (node[0], node[1], node[2] );
164       }
165       else if (nbnode == 4) {
166         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
168       }
169       else if (nbnode == 6) {
170         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171                                                node[4], node[5], ID);
172         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
173                                                node[4], node[5] );
174       }
175       else if (nbnode == 7) {
176         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177                                                node[4], node[5], node[6], ID);
178         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
179                                                node[4], node[5], node[6] );
180       }
181       else if (nbnode == 8) {
182         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183                                                node[4], node[5], node[6], node[7], ID);
184         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
185                                                node[4], node[5], node[6], node[7] );
186       }
187       else if (nbnode == 9) {
188         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189                                                node[4], node[5], node[6], node[7], node[8], ID);
190         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
191                                                node[4], node[5], node[6], node[7], node[8] );
192       }
193     } else {
194       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195       else           e = mesh->AddPolygonalFace      (node    );
196     }
197     break;
198
199   case SMDSAbs_Volume:
200     if ( !isPoly ) {
201       if      (nbnode == 4) {
202         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
204       }
205       else if (nbnode == 5) {
206         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207                                                  node[4], ID);
208         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
209                                                  node[4] );
210       }
211       else if (nbnode == 6) {
212         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213                                                  node[4], node[5], ID);
214         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
215                                                  node[4], node[5] );
216       }
217       else if (nbnode == 8) {
218         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7], ID);
220         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7] );
222       }
223       else if (nbnode == 10) {
224         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], ID);
227         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
228                                                  node[4], node[5], node[6], node[7],
229                                                  node[8], node[9] );
230       }
231       else if (nbnode == 12) {
232         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233                                                  node[4], node[5], node[6], node[7],
234                                                  node[8], node[9], node[10], node[11], ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10], node[11] );
238       }
239       else if (nbnode == 13) {
240         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241                                                  node[4], node[5], node[6], node[7],
242                                                  node[8], node[9], node[10],node[11],
243                                                  node[12],ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
245                                                  node[4], node[5], node[6], node[7],
246                                                  node[8], node[9], node[10],node[11],
247                                                  node[12] );
248       }
249       else if (nbnode == 15) {
250         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251                                                  node[4], node[5], node[6], node[7],
252                                                  node[8], node[9], node[10],node[11],
253                                                  node[12],node[13],node[14],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14] );
258       }
259       else if (nbnode == 20) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7],
262                                                  node[8], node[9], node[10],node[11],
263                                                  node[12],node[13],node[14],node[15],
264                                                  node[16],node[17],node[18],node[19],ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], node[10],node[11],
268                                                  node[12],node[13],node[14],node[15],
269                                                  node[16],node[17],node[18],node[19] );
270       }
271       else if (nbnode == 27) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10],node[11],
275                                                  node[12],node[13],node[14],node[15],
276                                                  node[16],node[17],node[18],node[19],
277                                                  node[20],node[21],node[22],node[23],
278                                                  node[24],node[25],node[26], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],node[13],node[14],node[15],
283                                                  node[16],node[17],node[18],node[19],
284                                                  node[20],node[21],node[22],node[23],
285                                                  node[24],node[25],node[26] );
286       }
287     }
288     break;
289
290   case SMDSAbs_Edge:
291     if ( nbnode == 2 ) {
292       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293       else           e = mesh->AddEdge      (node[0], node[1] );
294     }
295     else if ( nbnode == 3 ) {
296       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
298     }
299     break;
300
301   case SMDSAbs_0DElement:
302     if ( nbnode == 1 ) {
303       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304       else           e = mesh->Add0DElement      (node[0] );
305     }
306     break;
307
308   case SMDSAbs_Node:
309     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
311     break;
312
313   case SMDSAbs_Ball:
314     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315     else           e = mesh->AddBall      (node[0], ballDiameter);
316     break;
317
318   default:;
319   }
320   if ( e ) myLastCreatedElems.Append( e );
321   return e;
322 }
323
324 //=======================================================================
325 /*!
326  * \brief Add element
327  */
328 //=======================================================================
329
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
331                                                const SMDSAbs_ElementType type,
332                                                const bool                isPoly,
333                                                const int                 ID)
334 {
335   vector<const SMDS_MeshNode*> nodes;
336   nodes.reserve( nodeIDs.size() );
337   vector<int>::const_iterator id = nodeIDs.begin();
338   while ( id != nodeIDs.end() ) {
339     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340       nodes.push_back( node );
341     else
342       return 0;
343   }
344   return AddElement( nodes, type, isPoly, ID );
345 }
346
347 //=======================================================================
348 //function : Remove
349 //purpose  : Remove a node or an element.
350 //           Modify a compute state of sub-meshes which become empty
351 //=======================================================================
352
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
354                               const bool         isNodes )
355 {
356   myLastCreatedElems.Clear();
357   myLastCreatedNodes.Clear();
358
359   SMESHDS_Mesh* aMesh = GetMeshDS();
360   set< SMESH_subMesh *> smmap;
361
362   int removed = 0;
363   list<int>::const_iterator it = theIDs.begin();
364   for ( ; it != theIDs.end(); it++ ) {
365     const SMDS_MeshElement * elem;
366     if ( isNodes )
367       elem = aMesh->FindNode( *it );
368     else
369       elem = aMesh->FindElement( *it );
370     if ( !elem )
371       continue;
372
373     // Notify VERTEX sub-meshes about modification
374     if ( isNodes ) {
375       const SMDS_MeshNode* node = cast2Node( elem );
376       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377         if ( int aShapeID = node->getshapeId() )
378           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
379             smmap.insert( sm );
380     }
381     // Find sub-meshes to notify about modification
382     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383     //     while ( nodeIt->more() ) {
384     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
386     //       if ( aPosition.get() ) {
387     //         if ( int aShapeID = aPosition->GetShapeId() ) {
388     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389     //             smmap.insert( sm );
390     //         }
391     //       }
392     //     }
393
394     // Do remove
395     if ( isNodes )
396       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397     else
398       aMesh->RemoveElement( elem );
399     removed++;
400   }
401
402   // Notify sub-meshes about modification
403   if ( !smmap.empty() ) {
404     set< SMESH_subMesh *>::iterator smIt;
405     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
407   }
408
409   //   // Check if the whole mesh becomes empty
410   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
412
413   return removed;
414 }
415
416 //================================================================================
417 /*!
418  * \brief Create 0D elements on all nodes of the given object except those
419  *        nodes on which a 0D element already exists.
420  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
421  *                    the all mesh is treated
422  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423  */
424 //================================================================================
425
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427                                                    TIDSortedElemSet&       all0DElems )
428 {
429   SMDS_ElemIteratorPtr elemIt;
430   vector< const SMDS_MeshElement* > allNodes;
431   if ( elements.empty() )
432   {
433     allNodes.reserve( GetMeshDS()->NbNodes() );
434     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435     while ( elemIt->more() )
436       allNodes.push_back( elemIt->next() );
437
438     elemIt = elemSetIterator( allNodes );
439   }
440   else
441   {
442     elemIt = elemSetIterator( elements );
443   }
444
445   while ( elemIt->more() )
446   {
447     const SMDS_MeshElement* e = elemIt->next();
448     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449     while ( nodeIt->more() )
450     {
451       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453       if ( it0D->more() )
454         all0DElems.insert( it0D->next() );
455       else {
456         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457         all0DElems.insert( myLastCreatedElems.Last() );
458       }
459     }
460   }
461 }
462
463 //=======================================================================
464 //function : FindShape
465 //purpose  : Return an index of the shape theElem is on
466 //           or zero if a shape not found
467 //=======================================================================
468
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 {
471   myLastCreatedElems.Clear();
472   myLastCreatedNodes.Clear();
473
474   SMESHDS_Mesh * aMesh = GetMeshDS();
475   if ( aMesh->ShapeToMesh().IsNull() )
476     return 0;
477
478   int aShapeID = theElem->getshapeId();
479   if ( aShapeID < 1 )
480     return 0;
481
482   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483     if ( sm->Contains( theElem ))
484       return aShapeID;
485
486   if ( theElem->GetType() == SMDSAbs_Node ) {
487     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
488   }
489   else {
490     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
491   }
492
493   TopoDS_Shape aShape; // the shape a node of theElem is on
494   if ( theElem->GetType() != SMDSAbs_Node )
495   {
496     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497     while ( nodeIt->more() ) {
498       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499       if ((aShapeID = node->getshapeId()) > 0) {
500         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501           if ( sm->Contains( theElem ))
502             return aShapeID;
503           if ( aShape.IsNull() )
504             aShape = aMesh->IndexToShape( aShapeID );
505         }
506       }
507     }
508   }
509
510   // None of nodes is on a proper shape,
511   // find the shape among ancestors of aShape on which a node is
512   if ( !aShape.IsNull() ) {
513     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514     for ( ; ancIt.More(); ancIt.Next() ) {
515       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516       if ( sm && sm->Contains( theElem ))
517         return aMesh->ShapeToIndex( ancIt.Value() );
518     }
519   }
520   else
521   {
522     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523     while ( const SMESHDS_SubMesh* sm = smIt->next() )
524       if ( sm->Contains( theElem ))
525         return sm->GetID();
526   }
527
528   return 0;
529 }
530
531 //=======================================================================
532 //function : IsMedium
533 //purpose  :
534 //=======================================================================
535
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
537                                 const SMDSAbs_ElementType typeToCheck)
538 {
539   bool isMedium = false;
540   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541   while (it->more() && !isMedium ) {
542     const SMDS_MeshElement* elem = it->next();
543     isMedium = elem->IsMediumNode(node);
544   }
545   return isMedium;
546 }
547
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose  : Shift nodes in the array corresponded to quadratic triangle
551 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
553
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
555 {
556   const SMDS_MeshNode* nd1 = aNodes[0];
557   aNodes[0] = aNodes[1];
558   aNodes[1] = aNodes[2];
559   aNodes[2] = nd1;
560   const SMDS_MeshNode* nd2 = aNodes[3];
561   aNodes[3] = aNodes[4];
562   aNodes[4] = aNodes[5];
563   aNodes[5] = nd2;
564 }
565
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose  : return number of the edges connected with the theNode.
569 //           if theEdges has connections with the other type of the
570 //           elements, return -1
571 //=======================================================================
572
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
574 {
575   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
576   // int nb=0;
577   // while(elemIt->more()) {
578   //   elemIt->next();
579   //   nb++;
580   // }
581   // return nb;
582   return theNode->NbInverseElements();
583 }
584
585 //=======================================================================
586 //function : getNodesFromTwoTria
587 //purpose  : 
588 //=======================================================================
589
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591                                 const SMDS_MeshElement * theTria2,
592                                 vector< const SMDS_MeshNode*>& N1,
593                                 vector< const SMDS_MeshNode*>& N2)
594 {
595   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596   if ( N1.size() < 6 ) return false;
597   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598   if ( N2.size() < 6 ) return false;
599
600   int sames[3] = {-1,-1,-1};
601   int nbsames = 0;
602   int i, j;
603   for(i=0; i<3; i++) {
604     for(j=0; j<3; j++) {
605       if(N1[i]==N2[j]) {
606         sames[i] = j;
607         nbsames++;
608         break;
609       }
610     }
611   }
612   if(nbsames!=2) return false;
613   if(sames[0]>-1) {
614     shiftNodesQuadTria(N1);
615     if(sames[1]>-1) {
616       shiftNodesQuadTria(N1);
617     }
618   }
619   i = sames[0] + sames[1] + sames[2];
620   for(; i<2; i++) {
621     shiftNodesQuadTria(N2);
622   }
623   // now we receive following N1 and N2 (using numeration as in the image below)
624   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
625   // i.e. first nodes from both arrays form a new diagonal
626   return true;
627 }
628
629 //=======================================================================
630 //function : InverseDiag
631 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
632 //           but having other common link.
633 //           Return False if args are improper
634 //=======================================================================
635
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637                                     const SMDS_MeshElement * theTria2 )
638 {
639   MESSAGE("InverseDiag");
640   myLastCreatedElems.Clear();
641   myLastCreatedNodes.Clear();
642
643   if (!theTria1 || !theTria2)
644     return false;
645
646   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647   if (!F1) return false;
648   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649   if (!F2) return false;
650   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
652
653     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
655     //    |/ |                                         | \|
656     //  B +--+ 2                                     B +--+ 2
657
658     // put nodes in array and find out indices of the same ones
659     const SMDS_MeshNode* aNodes [6];
660     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
661     int i = 0;
662     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663     while ( it->more() ) {
664       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
665
666       if ( i > 2 ) // theTria2
667         // find same node of theTria1
668         for ( int j = 0; j < 3; j++ )
669           if ( aNodes[ i ] == aNodes[ j ]) {
670             sameInd[ j ] = i;
671             sameInd[ i ] = j;
672             break;
673           }
674       // next
675       i++;
676       if ( i == 3 ) {
677         if ( it->more() )
678           return false; // theTria1 is not a triangle
679         it = theTria2->nodesIterator();
680       }
681       if ( i == 6 && it->more() )
682         return false; // theTria2 is not a triangle
683     }
684
685     // find indices of 1,2 and of A,B in theTria1
686     int iA = -1, iB = 0, i1 = 0, i2 = 0;
687     for ( i = 0; i < 6; i++ ) {
688       if ( sameInd [ i ] == -1 ) {
689         if ( i < 3 ) i1 = i;
690         else         i2 = i;
691       }
692       else if (i < 3) {
693         if ( iA >= 0) iB = i;
694         else          iA = i;
695       }
696     }
697     // nodes 1 and 2 should not be the same
698     if ( aNodes[ i1 ] == aNodes[ i2 ] )
699       return false;
700
701     // theTria1: A->2
702     aNodes[ iA ] = aNodes[ i2 ];
703     // theTria2: B->1
704     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
705
706     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
708
709     return true;
710
711   } // end if(F1 && F2)
712
713   // check case of quadratic faces
714   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
716     return false;
717   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
719     return false;
720
721   //       5
722   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724   //    |   / |
725   //  7 +  +  + 6
726   //    | /9  |
727   //    |/    |
728   //  4 +--+--+ 3
729   //       8
730
731   vector< const SMDS_MeshNode* > N1;
732   vector< const SMDS_MeshNode* > N2;
733   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
734     return false;
735   // now we receive following N1 and N2 (using numeration as above image)
736   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
737   // i.e. first nodes from both arrays determ new diagonal
738
739   vector< const SMDS_MeshNode*> N1new( N1.size() );
740   vector< const SMDS_MeshNode*> N2new( N2.size() );
741   N1new.back() = N1.back(); // central node of biquadratic
742   N2new.back() = N2.back();
743   N1new[0] = N1[0];  N2new[0] = N1[0];
744   N1new[1] = N2[0];  N2new[1] = N1[1];
745   N1new[2] = N2[1];  N2new[2] = N2[0];
746   N1new[3] = N1[4];  N2new[3] = N1[3];
747   N1new[4] = N2[3];  N2new[4] = N2[5];
748   N1new[5] = N1[5];  N2new[5] = N1[4];
749   // change nodes in faces
750   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
752
753   // move the central node of biquadratic triangle
754   SMESH_MesherHelper helper( *GetMesh() );
755   for ( int is2nd = 0; is2nd < 2; ++is2nd )
756   {
757     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
758     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759     if ( nodes.size() < 7 )
760       continue;
761     helper.SetSubShape( tria->getshapeId() );
762     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
763     gp_Pnt xyz;
764     if ( F.IsNull() )
765     {
766       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767               SMESH_TNodeXYZ( nodes[4] ) +
768               SMESH_TNodeXYZ( nodes[5] )) / 3.;
769     }
770     else
771     {
772       bool checkUV;
773       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
776       TopLoc_Location loc;
777       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778       xyz = S->Value( uv.X(), uv.Y() );
779       xyz.Transform( loc );
780       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
781            nodes[6]->getshapeId() > 0 )
782         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
783     }
784     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
785   }
786   return true;
787 }
788
789 //=======================================================================
790 //function : findTriangles
791 //purpose  : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
793
794 static bool findTriangles(const SMDS_MeshNode *    theNode1,
795                           const SMDS_MeshNode *    theNode2,
796                           const SMDS_MeshElement*& theTria1,
797                           const SMDS_MeshElement*& theTria2)
798 {
799   if ( !theNode1 || !theNode2 ) return false;
800
801   theTria1 = theTria2 = 0;
802
803   set< const SMDS_MeshElement* > emap;
804   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
805   while (it->more()) {
806     const SMDS_MeshElement* elem = it->next();
807     if ( elem->NbCornerNodes() == 3 )
808       emap.insert( elem );
809   }
810   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
811   while (it->more()) {
812     const SMDS_MeshElement* elem = it->next();
813     if ( emap.count( elem )) {
814       if ( !theTria1 )
815       {
816         theTria1 = elem;
817       }
818       else  
819       {
820         theTria2 = elem;
821         // theTria1 must be element with minimum ID
822         if ( theTria2->GetID() < theTria1->GetID() )
823           std::swap( theTria2, theTria1 );
824         return true;
825       }
826     }
827   }
828   return false;
829 }
830
831 //=======================================================================
832 //function : InverseDiag
833 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
834 //           with ones built on the same 4 nodes but having other common link.
835 //           Return false if proper faces not found
836 //=======================================================================
837
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839                                     const SMDS_MeshNode * theNode2)
840 {
841   myLastCreatedElems.Clear();
842   myLastCreatedNodes.Clear();
843
844   MESSAGE( "::InverseDiag()" );
845
846   const SMDS_MeshElement *tr1, *tr2;
847   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
848     return false;
849
850   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851   if (!F1) return false;
852   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853   if (!F2) return false;
854   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
856
857     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
859     //    |/ |                                    | \|
860     //  B +--+ 2                                B +--+ 2
861
862     // put nodes in array
863     // and find indices of 1,2 and of A in tr1 and of B in tr2
864     int i, iA1 = 0, i1 = 0;
865     const SMDS_MeshNode* aNodes1 [3];
866     SMDS_ElemIteratorPtr it;
867     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869       if ( aNodes1[ i ] == theNode1 )
870         iA1 = i; // node A in tr1
871       else if ( aNodes1[ i ] != theNode2 )
872         i1 = i;  // node 1
873     }
874     int iB2 = 0, i2 = 0;
875     const SMDS_MeshNode* aNodes2 [3];
876     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878       if ( aNodes2[ i ] == theNode2 )
879         iB2 = i; // node B in tr2
880       else if ( aNodes2[ i ] != theNode1 )
881         i2 = i;  // node 2
882     }
883
884     // nodes 1 and 2 should not be the same
885     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
886       return false;
887
888     // tr1: A->2
889     aNodes1[ iA1 ] = aNodes2[ i2 ];
890     // tr2: B->1
891     aNodes2[ iB2 ] = aNodes1[ i1 ];
892
893     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
895
896     return true;
897   }
898
899   // check case of quadratic faces
900   return InverseDiag(tr1,tr2);
901 }
902
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
906 //           fusion of triangles tr1 and tr2 having shared link on
907 //           theNode1 and theNode2
908 //=======================================================================
909
910 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
911                         const SMDS_MeshNode *    theNode1,
912                         const SMDS_MeshNode *    theNode2,
913                         const SMDS_MeshElement * tr1,
914                         const SMDS_MeshElement * tr2 )
915 {
916   if( tr1->NbNodes() != tr2->NbNodes() )
917     return false;
918   // find the 4-th node to insert into tr1
919   const SMDS_MeshNode* n4 = 0;
920   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
921   int i=0;
922   while ( !n4 && i<3 ) {
923     const SMDS_MeshNode * n = cast2Node( it->next() );
924     i++;
925     bool isDiag = ( n == theNode1 || n == theNode2 );
926     if ( !isDiag )
927       n4 = n;
928   }
929   // Make an array of nodes to be in a quadrangle
930   int iNode = 0, iFirstDiag = -1;
931   it = tr1->nodesIterator();
932   i=0;
933   while ( i<3 ) {
934     const SMDS_MeshNode * n = cast2Node( it->next() );
935     i++;
936     bool isDiag = ( n == theNode1 || n == theNode2 );
937     if ( isDiag ) {
938       if ( iFirstDiag < 0 )
939         iFirstDiag = iNode;
940       else if ( iNode - iFirstDiag == 1 )
941         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
942     }
943     else if ( n == n4 ) {
944       return false; // tr1 and tr2 should not have all the same nodes
945     }
946     theQuadNodes[ iNode++ ] = n;
947   }
948   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949     theQuadNodes[ iNode ] = n4;
950
951   return true;
952 }
953
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
957 //           with a quadrangle built on the same 4 nodes.
958 //           Return false if proper faces not found
959 //=======================================================================
960
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962                                    const SMDS_MeshNode * theNode2)
963 {
964   myLastCreatedElems.Clear();
965   myLastCreatedNodes.Clear();
966
967   MESSAGE( "::DeleteDiag()" );
968
969   const SMDS_MeshElement *tr1, *tr2;
970   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
971     return false;
972
973   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974   if (!F1) return false;
975   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976   if (!F2) return false;
977   SMESHDS_Mesh * aMesh = GetMeshDS();
978
979   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
981
982     const SMDS_MeshNode* aNodes [ 4 ];
983     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
984       return false;
985
986     const SMDS_MeshElement* newElem = 0;
987     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988     myLastCreatedElems.Append(newElem);
989     AddToSameGroups( newElem, tr1, aMesh );
990     int aShapeId = tr1->getshapeId();
991     if ( aShapeId )
992       {
993         aMesh->SetMeshElementOnShape( newElem, aShapeId );
994       }
995     aMesh->RemoveElement( tr1 );
996     aMesh->RemoveElement( tr2 );
997
998     return true;
999   }
1000
1001   // check case of quadratic faces
1002   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1003     return false;
1004   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1005     return false;
1006
1007   //       5
1008   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1010   //    |   / |
1011   //  7 +  +  + 6
1012   //    | /9  |
1013   //    |/    |
1014   //  4 +--+--+ 3
1015   //       8
1016
1017   vector< const SMDS_MeshNode* > N1;
1018   vector< const SMDS_MeshNode* > N2;
1019   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1020     return false;
1021   // now we receive following N1 and N2 (using numeration as above image)
1022   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1023   // i.e. first nodes from both arrays determ new diagonal
1024
1025   const SMDS_MeshNode* aNodes[8];
1026   aNodes[0] = N1[0];
1027   aNodes[1] = N1[1];
1028   aNodes[2] = N2[0];
1029   aNodes[3] = N2[1];
1030   aNodes[4] = N1[3];
1031   aNodes[5] = N2[5];
1032   aNodes[6] = N2[3];
1033   aNodes[7] = N1[5];
1034
1035   const SMDS_MeshElement* newElem = 0;
1036   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038   myLastCreatedElems.Append(newElem);
1039   AddToSameGroups( newElem, tr1, aMesh );
1040   int aShapeId = tr1->getshapeId();
1041   if ( aShapeId )
1042     {
1043       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044     }
1045   aMesh->RemoveElement( tr1 );
1046   aMesh->RemoveElement( tr2 );
1047
1048   // remove middle node (9)
1049   GetMeshDS()->RemoveNode( N1[4] );
1050
1051   return true;
1052 }
1053
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose  : Reverse theElement orientation
1057 //=======================================================================
1058
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1060 {
1061   MESSAGE("Reorient");
1062   myLastCreatedElems.Clear();
1063   myLastCreatedNodes.Clear();
1064
1065   if (!theElem)
1066     return false;
1067   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068   if ( !it || !it->more() )
1069     return false;
1070
1071   const SMDSAbs_ElementType type = theElem->GetType();
1072   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1073     return false;
1074
1075   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1077   {
1078     const SMDS_VtkVolume* aPolyedre =
1079       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1080     if (!aPolyedre) {
1081       MESSAGE("Warning: bad volumic element");
1082       return false;
1083     }
1084     const int nbFaces = aPolyedre->NbFaces();
1085     vector<const SMDS_MeshNode *> poly_nodes;
1086     vector<int> quantities (nbFaces);
1087
1088     // reverse each face of the polyedre
1089     for (int iface = 1; iface <= nbFaces; iface++) {
1090       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091       quantities[iface - 1] = nbFaceNodes;
1092
1093       for (inode = nbFaceNodes; inode >= 1; inode--) {
1094         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095         poly_nodes.push_back(curNode);
1096       }
1097     }
1098     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099   }
1100   else // other elements
1101   {
1102     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104     if ( interlace.empty() )
1105     {
1106       std::reverse( nodes.begin(), nodes.end() ); // polygon
1107     }
1108     else if ( interlace.size() > 1 )
1109     {
1110       SMDS_MeshCell::applyInterlace( interlace, nodes );
1111     }
1112     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1113   }
1114   return false;
1115 }
1116
1117 //================================================================================
1118 /*!
1119  * \brief Reorient faces.
1120  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121  * \param theDirection - desired direction of normal of \a theFace
1122  * \param theFace - one of \a theFaces that sould be oriented according to
1123  *        \a theDirection and whose orientation defines orientation of other faces
1124  * \return number of reoriented faces.
1125  */
1126 //================================================================================
1127
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1129                                   const gp_Dir&            theDirection,
1130                                   const SMDS_MeshElement * theFace)
1131 {
1132   int nbReori = 0;
1133   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134
1135   if ( theFaces.empty() )
1136   {
1137     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138     while ( fIt->more() )
1139       theFaces.insert( theFaces.end(), fIt->next() );
1140   }
1141
1142   // orient theFace according to theDirection
1143   gp_XYZ normal;
1144   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145   if ( normal * theDirection.XYZ() < 0 )
1146     nbReori += Reorient( theFace );
1147
1148   // Orient other faces
1149
1150   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151   TIDSortedElemSet avoidSet;
1152   set< SMESH_TLink > checkedLinks;
1153   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154
1155   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156     theFaces.erase( theFace );
1157   startFaces.insert( theFace );
1158
1159   int nodeInd1, nodeInd2;
1160   const SMDS_MeshElement*           otherFace;
1161   vector< const SMDS_MeshElement* > facesNearLink;
1162   vector< std::pair< int, int > >   nodeIndsOfFace;
1163
1164   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165   while ( !startFaces.empty() )
1166   {
1167     startFace = startFaces.begin();
1168     theFace = *startFace;
1169     startFaces.erase( startFace );
1170     if ( !visitedFaces.insert( theFace ).second )
1171       continue;
1172
1173     avoidSet.clear();
1174     avoidSet.insert(theFace);
1175
1176     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1177
1178     const int nbNodes = theFace->NbCornerNodes();
1179     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180     {
1181       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182       linkIt_isNew = checkedLinks.insert( link );
1183       if ( !linkIt_isNew.second )
1184       {
1185         // link has already been checked and won't be encountered more
1186         // if the group (theFaces) is manifold
1187         //checkedLinks.erase( linkIt_isNew.first );
1188       }
1189       else
1190       {
1191         facesNearLink.clear();
1192         nodeIndsOfFace.clear();
1193         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1194                                                              theFaces, avoidSet,
1195                                                              &nodeInd1, &nodeInd2 )))
1196           if ( otherFace != theFace)
1197           {
1198             facesNearLink.push_back( otherFace );
1199             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200             avoidSet.insert( otherFace );
1201           }
1202         if ( facesNearLink.size() > 1 )
1203         {
1204           // NON-MANIFOLD mesh shell !
1205           // select a face most co-directed with theFace,
1206           // other faces won't be visited this time
1207           gp_XYZ NF, NOF;
1208           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209           double proj, maxProj = -1;
1210           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212             if (( proj = Abs( NF * NOF )) > maxProj ) {
1213               maxProj = proj;
1214               otherFace = facesNearLink[i];
1215               nodeInd1  = nodeIndsOfFace[i].first;
1216               nodeInd2  = nodeIndsOfFace[i].second;
1217             }
1218           }
1219           // not to visit rejected faces
1220           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222               visitedFaces.insert( facesNearLink[i] );
1223         }
1224         else if ( facesNearLink.size() == 1 )
1225         {
1226           otherFace = facesNearLink[0];
1227           nodeInd1  = nodeIndsOfFace.back().first;
1228           nodeInd2  = nodeIndsOfFace.back().second;
1229         }
1230         if ( otherFace && otherFace != theFace)
1231         {
1232           // link must be reverse in otherFace if orientation ot otherFace
1233           // is same as that of theFace
1234           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1235           {
1236             nbReori += Reorient( otherFace );
1237           }
1238           startFaces.insert( otherFace );
1239         }
1240       }
1241       std::swap( link.first, link.second ); // reverse the link
1242     }
1243   }
1244   return nbReori;
1245 }
1246
1247 //================================================================================
1248 /*!
1249  * \brief Reorient faces basing on orientation of adjacent volumes.
1250  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251  * \param theVolumes - reference volumes.
1252  * \param theOutsideNormal - to orient faces to have their normal
1253  *        pointing either \a outside or \a inside the adjacent volumes.
1254  * \return number of reoriented faces.
1255  */
1256 //================================================================================
1257
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259                                       TIDSortedElemSet & theVolumes,
1260                                       const bool         theOutsideNormal)
1261 {
1262   int nbReori = 0;
1263
1264   SMDS_ElemIteratorPtr faceIt;
1265   if ( theFaces.empty() )
1266     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1267   else
1268     faceIt = elemSetIterator( theFaces );
1269
1270   vector< const SMDS_MeshNode* > faceNodes;
1271   TIDSortedElemSet checkedVolumes;
1272   set< const SMDS_MeshNode* > faceNodesSet;
1273   SMDS_VolumeTool volumeTool;
1274
1275   while ( faceIt->more() ) // loop on given faces
1276   {
1277     const SMDS_MeshElement* face = faceIt->next();
1278     if ( face->GetType() != SMDSAbs_Face )
1279       continue;
1280
1281     const int nbCornersNodes = face->NbCornerNodes();
1282     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1283
1284     checkedVolumes.clear();
1285     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286     while ( vIt->more() )
1287     {
1288       const SMDS_MeshElement* volume = vIt->next();
1289
1290       if ( !checkedVolumes.insert( volume ).second )
1291         continue;
1292       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1293         continue;
1294
1295       // is volume adjacent?
1296       bool allNodesCommon = true;
1297       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299       if ( !allNodesCommon )
1300         continue;
1301
1302       // get nodes of a corresponding volume facet
1303       faceNodesSet.clear();
1304       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305       volumeTool.Set( volume );
1306       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307       if ( facetID < 0 ) continue;
1308       volumeTool.SetExternalNormal();
1309       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1310
1311       // compare order of faceNodes and facetNodes
1312       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1313       int iNN[2];
1314       for ( int i = 0; i < 2; ++i )
1315       {
1316         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318           if ( faceNodes[ iN ] == n )
1319           {
1320             iNN[ i ] = iN;
1321             break;
1322           }
1323       }
1324       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325       if ( isOutside != theOutsideNormal )
1326         nbReori += Reorient( face );
1327     }
1328   }  // loop on given faces
1329
1330   return nbReori;
1331 }
1332
1333 //=======================================================================
1334 //function : getBadRate
1335 //purpose  :
1336 //=======================================================================
1337
1338 static double getBadRate (const SMDS_MeshElement*               theElem,
1339                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1340 {
1341   SMESH::Controls::TSequenceOfXYZ P;
1342   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1343     return 1e100;
1344   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1346 }
1347
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose  : Cut quadrangles into triangles.
1351 //           theCrit is used to select a diagonal to cut
1352 //=======================================================================
1353
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1355                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1356 {
1357   myLastCreatedElems.Clear();
1358   myLastCreatedNodes.Clear();
1359
1360   if ( !theCrit.get() )
1361     return false;
1362
1363   SMESHDS_Mesh * aMesh = GetMeshDS();
1364
1365   Handle(Geom_Surface) surface;
1366   SMESH_MesherHelper   helper( *GetMesh() );
1367
1368   TIDSortedElemSet::iterator itElem;
1369   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1370   {
1371     const SMDS_MeshElement* elem = *itElem;
1372     if ( !elem || elem->GetType() != SMDSAbs_Face )
1373       continue;
1374     if ( elem->NbCornerNodes() != 4 )
1375       continue;
1376
1377     // retrieve element nodes
1378     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1379
1380     // compare two sets of possible triangles
1381     double aBadRate1, aBadRate2; // to what extent a set is bad
1382     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1385
1386     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1389
1390     const int aShapeId = FindShape( elem );
1391     const SMDS_MeshElement* newElem1 = 0;
1392     const SMDS_MeshElement* newElem2 = 0;
1393
1394     if ( !elem->IsQuadratic() ) // split liner quadrangle
1395     {
1396       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398       if ( aBadRate1 <= aBadRate2 ) {
1399         // tr1 + tr2 is better
1400         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1402       }
1403       else {
1404         // tr3 + tr4 is better
1405         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1407       }
1408     }
1409     else // split quadratic quadrangle
1410     {
1411       helper.SetIsQuadratic( true );
1412       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1413
1414       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415       if ( aNodes.size() == 9 )
1416       {
1417         helper.SetIsBiQuadratic( true );
1418         if ( aBadRate1 <= aBadRate2 )
1419           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1420         else
1421           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1422       }
1423       // create a new element
1424       if ( aBadRate1 <= aBadRate2 ) {
1425         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1427       }
1428       else {
1429         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1431       }
1432     } // quadratic case
1433
1434     // care of a new element
1435
1436     myLastCreatedElems.Append(newElem1);
1437     myLastCreatedElems.Append(newElem2);
1438     AddToSameGroups( newElem1, elem, aMesh );
1439     AddToSameGroups( newElem2, elem, aMesh );
1440
1441     // put a new triangle on the same shape
1442     if ( aShapeId )
1443       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1445
1446     aMesh->RemoveElement( elem );
1447   }
1448   return true;
1449 }
1450
1451 //=======================================================================
1452 /*!
1453  * \brief Split each of given quadrangles into 4 triangles.
1454  * \param theElems - The faces to be splitted. If empty all faces are split.
1455  */
1456 //=======================================================================
1457
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1459 {
1460   myLastCreatedElems.Clear();
1461   myLastCreatedNodes.Clear();
1462
1463   SMESH_MesherHelper helper( *GetMesh() );
1464   helper.SetElementsOnShape( true );
1465
1466   SMDS_ElemIteratorPtr faceIt;
1467   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468   else                    faceIt = elemSetIterator( theElems );
1469
1470   bool   checkUV;
1471   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1472   gp_XYZ xyz[9];
1473   vector< const SMDS_MeshNode* > nodes;
1474   SMESHDS_SubMesh*               subMeshDS;
1475   TopoDS_Face                    F;
1476   Handle(Geom_Surface)           surface;
1477   TopLoc_Location                loc;
1478
1479   while ( faceIt->more() )
1480   {
1481     const SMDS_MeshElement* quad = faceIt->next();
1482     if ( !quad || quad->NbCornerNodes() != 4 )
1483       continue;
1484
1485     // get a surface the quad is on
1486
1487     if ( quad->getshapeId() < 1 )
1488     {
1489       F.Nullify();
1490       helper.SetSubShape( 0 );
1491       subMeshDS = 0;
1492     }
1493     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1494     {
1495       helper.SetSubShape( quad->getshapeId() );
1496       if ( !helper.GetSubShape().IsNull() &&
1497            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1498       {
1499         F = TopoDS::Face( helper.GetSubShape() );
1500         surface = BRep_Tool::Surface( F, loc );
1501         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1502       }
1503       else
1504       {
1505         helper.SetSubShape( 0 );
1506         subMeshDS = 0;
1507       }
1508     }
1509
1510     // create a central node
1511
1512     const SMDS_MeshNode* nCentral;
1513     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1514
1515     if ( nodes.size() == 9 )
1516     {
1517       nCentral = nodes.back();
1518     }
1519     else
1520     {
1521       size_t iN = 0;
1522       if ( F.IsNull() )
1523       {
1524         for ( ; iN < nodes.size(); ++iN )
1525           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1526
1527         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1529
1530         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531                                    xyz[0], xyz[1], xyz[2], xyz[3],
1532                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1533       }
1534       else
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1538
1539         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1541
1542         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                   uv[0], uv[1], uv[2], uv[3],
1544                                   uv[4], uv[5], uv[6], uv[7] );
1545
1546         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1547         xyz[ 8 ] = p.XYZ();
1548       }
1549
1550       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551                                  uv[8].X(), uv[8].Y() );
1552       myLastCreatedNodes.Append( nCentral );
1553     }
1554
1555     // create 4 triangles
1556
1557     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1558     
1559     helper.SetIsQuadratic  ( nodes.size() > 4 );
1560     helper.SetIsBiQuadratic( nodes.size() == 9 );
1561     if ( helper.GetIsQuadratic() )
1562       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1563
1564     for ( int i = 0; i < 4; ++i )
1565     {
1566       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1567                                                nodes[(i+1)%4],
1568                                                nCentral );
1569       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570       myLastCreatedElems.Append( tria );
1571     }
1572   }
1573 }
1574
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose  : Find better diagonal for cutting.
1578 //=======================================================================
1579
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1581                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1582 {
1583   myLastCreatedElems.Clear();
1584   myLastCreatedNodes.Clear();
1585
1586   if (!theCrit.get())
1587     return -1;
1588
1589   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1590     return -1;
1591
1592   if( theQuad->NbNodes()==4 ||
1593       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1594
1595     // retrieve element nodes
1596     const SMDS_MeshNode* aNodes [4];
1597     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1598     int i = 0;
1599     //while (itN->more())
1600     while (i<4) {
1601       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1602     }
1603     // compare two sets of possible triangles
1604     double aBadRate1, aBadRate2; // to what extent a set is bad
1605     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1608
1609     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615       return 1; // diagonal 1-3
1616
1617     return 2; // diagonal 2-4
1618   }
1619   return -1;
1620 }
1621
1622 namespace
1623 {
1624   // Methods of splitting volumes into tetra
1625
1626   const int theHexTo5_1[5*4+1] =
1627     {
1628       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1629     };
1630   const int theHexTo5_2[5*4+1] =
1631     {
1632       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1633     };
1634   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1635
1636   const int theHexTo6_1[6*4+1] =
1637     {
1638       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
1639     };
1640   const int theHexTo6_2[6*4+1] =
1641     {
1642       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
1643     };
1644   const int theHexTo6_3[6*4+1] =
1645     {
1646       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
1647     };
1648   const int theHexTo6_4[6*4+1] =
1649     {
1650       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
1651     };
1652   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1653
1654   const int thePyraTo2_1[2*4+1] =
1655     {
1656       0, 1, 2, 4,    0, 2, 3, 4,   -1
1657     };
1658   const int thePyraTo2_2[2*4+1] =
1659     {
1660       1, 2, 3, 4,    1, 3, 0, 4,   -1
1661     };
1662   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1663
1664   const int thePentaTo3_1[3*4+1] =
1665     {
1666       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1667     };
1668   const int thePentaTo3_2[3*4+1] =
1669     {
1670       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1671     };
1672   const int thePentaTo3_3[3*4+1] =
1673     {
1674       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1675     };
1676   const int thePentaTo3_4[3*4+1] =
1677     {
1678       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1679     };
1680   const int thePentaTo3_5[3*4+1] =
1681     {
1682       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1683     };
1684   const int thePentaTo3_6[3*4+1] =
1685     {
1686       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1687     };
1688   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1690
1691   // Methods of splitting hexahedron into prisms
1692
1693   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1694     {
1695       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
1696     };
1697   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1698     {
1699       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
1700     };
1701   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1702     {
1703       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1704     };
1705
1706   const int theHexTo2Prisms_BT_1[6*2+1] =
1707     {
1708       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1709     };
1710   const int theHexTo2Prisms_BT_2[6*2+1] =
1711     {
1712       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1713     };
1714   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1715
1716   const int theHexTo2Prisms_LR_1[6*2+1] =
1717     {
1718       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1719     };
1720   const int theHexTo2Prisms_LR_2[6*2+1] =
1721     {
1722       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1723     };
1724   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1725
1726   const int theHexTo2Prisms_FB_1[6*2+1] =
1727     {
1728       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1729     };
1730   const int theHexTo2Prisms_FB_2[6*2+1] =
1731     {
1732       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1733     };
1734   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1735
1736
1737   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1738   {
1739     int _n1, _n2, _n3;
1740     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1743                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1744   };
1745   struct TSplitMethod
1746   {
1747     int        _nbSplits;
1748     int        _nbCorners;
1749     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1751     bool       _ownConn;      //!< to delete _connectivity in destructor
1752     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1753
1754     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757     bool hasFacet( const TTriangleFacet& facet ) const
1758     {
1759       if ( _nbCorners == 4 )
1760       {
1761         const int* tetConn = _connectivity;
1762         for ( ; tetConn[0] >= 0; tetConn += 4 )
1763           if (( facet.contains( tetConn[0] ) +
1764                 facet.contains( tetConn[1] ) +
1765                 facet.contains( tetConn[2] ) +
1766                 facet.contains( tetConn[3] )) == 3 )
1767             return true;
1768       }
1769       else // prism, _nbCorners == 6
1770       {
1771         const int* prismConn = _connectivity;
1772         for ( ; prismConn[0] >= 0; prismConn += 6 )
1773         {
1774           if (( facet.contains( prismConn[0] ) &&
1775                 facet.contains( prismConn[1] ) &&
1776                 facet.contains( prismConn[2] ))
1777               ||
1778               ( facet.contains( prismConn[3] ) &&
1779                 facet.contains( prismConn[4] ) &&
1780                 facet.contains( prismConn[5] )))
1781             return true;
1782         }
1783       }
1784       return false;
1785     }
1786   };
1787
1788   //=======================================================================
1789   /*!
1790    * \brief return TSplitMethod for the given element to split into tetrahedra
1791    */
1792   //=======================================================================
1793
1794   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1795   {
1796     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1797
1798     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799     // an edge and a face barycenter; tertaherdons are based on triangles and
1800     // a volume barycenter
1801     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1802
1803     // Find out how adjacent volumes are split
1804
1805     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1808     {
1809       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811       if ( nbNodes < 4 ) continue;
1812
1813       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814       const int* nInd = vol.GetFaceNodesIndices( iF );
1815       if ( nbNodes == 4 )
1816       {
1817         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1821       }
1822       else
1823       {
1824         int iCom = 0; // common node of triangle faces to split into
1825         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1826         {
1827           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1828                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1829                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1831                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1832                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1834           {
1835             triaSplits.push_back( t012 );
1836             triaSplits.push_back( t023 );
1837             break;
1838           }
1839         }
1840       }
1841       if ( !triaSplits.empty() )
1842         hasAdjacentSplits = true;
1843     }
1844
1845     // Among variants of split method select one compliant with adjacent volumes
1846
1847     TSplitMethod method;
1848     if ( !vol.Element()->IsPoly() && !is24TetMode )
1849     {
1850       int nbVariants = 2, nbTet = 0;
1851       const int** connVariants = 0;
1852       switch ( vol.Element()->GetEntityType() )
1853       {
1854       case SMDSEntity_Hexa:
1855       case SMDSEntity_Quad_Hexa:
1856       case SMDSEntity_TriQuad_Hexa:
1857         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858           connVariants = theHexTo5, nbTet = 5;
1859         else
1860           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1861         break;
1862       case SMDSEntity_Pyramid:
1863       case SMDSEntity_Quad_Pyramid:
1864         connVariants = thePyraTo2;  nbTet = 2;
1865         break;
1866       case SMDSEntity_Penta:
1867       case SMDSEntity_Quad_Penta:
1868         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1869         break;
1870       default:
1871         nbVariants = 0;
1872       }
1873       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1874       {
1875         // check method compliancy with adjacent tetras,
1876         // all found splits must be among facets of tetras described by this method
1877         method = TSplitMethod( nbTet, connVariants[variant] );
1878         if ( hasAdjacentSplits && method._nbSplits > 0 )
1879         {
1880           bool facetCreated = true;
1881           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1882           {
1883             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885               facetCreated = method.hasFacet( *facet );
1886           }
1887           if ( !facetCreated )
1888             method = TSplitMethod(0); // incompatible method
1889         }
1890       }
1891     }
1892     if ( method._nbSplits < 1 )
1893     {
1894       // No standard method is applicable, use a generic solution:
1895       // each facet of a volume is split into triangles and
1896       // each of triangles and a volume barycenter form a tetrahedron.
1897
1898       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1899
1900       int* connectivity = new int[ maxTetConnSize + 1 ];
1901       method._connectivity = connectivity;
1902       method._ownConn = true;
1903       method._baryNode = !isHex27; // to create central node or not
1904
1905       int connSize = 0;
1906       int baryCenInd = vol.NbNodes() - int( isHex27 );
1907       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1908       {
1909         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910         const int*   nInd = vol.GetFaceNodesIndices( iF );
1911         // find common node of triangle facets of tetra to create
1912         int iCommon = 0; // index in linear numeration
1913         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914         if ( !triaSplits.empty() )
1915         {
1916           // by found facets
1917           const TTriangleFacet* facet = &triaSplits.front();
1918           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1921               break;
1922         }
1923         else if ( nbNodes > 3 && !is24TetMode )
1924         {
1925           // find the best method of splitting into triangles by aspect ratio
1926           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927           map< double, int > badness2iCommon;
1928           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1931           {
1932             double badness = 0;
1933             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1934             {
1935               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1936                                       nodes[ iQ*((iLast-1)%nbNodes)],
1937                                       nodes[ iQ*((iLast  )%nbNodes)]);
1938               badness += getBadRate( &tria, aspectRatio );
1939             }
1940             badness2iCommon.insert( make_pair( badness, iCommon ));
1941           }
1942           // use iCommon with lowest badness
1943           iCommon = badness2iCommon.begin()->second;
1944         }
1945         if ( iCommon >= nbNodes )
1946           iCommon = 0; // something wrong
1947
1948         // fill connectivity of tetrahedra based on a current face
1949         int nbTet = nbNodes - 2;
1950         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1951         {
1952           int faceBaryCenInd;
1953           if ( isHex27 )
1954           {
1955             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1957           }
1958           else
1959           {
1960             method._faceBaryNode[ iF ] = 0;
1961             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1962           }
1963           nbTet = nbNodes;
1964           for ( int i = 0; i < nbTet; ++i )
1965           {
1966             int i1 = i, i2 = (i+1) % nbNodes;
1967             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970             connectivity[ connSize++ ] = faceBaryCenInd;
1971             connectivity[ connSize++ ] = baryCenInd;
1972           }
1973         }
1974         else
1975         {
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         method._nbSplits += nbTet;
1987
1988       } // loop on volume faces
1989
1990       connectivity[ connSize++ ] = -1;
1991
1992     } // end of generic solution
1993
1994     return method;
1995   }
1996   //=======================================================================
1997   /*!
1998    * \brief return TSplitMethod to split haxhedron into prisms
1999    */
2000   //=======================================================================
2001
2002   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003                                     const int        methodFlags,
2004                                     const int        facetToSplit)
2005   {
2006     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2007     // B, T, L, B, R, F
2008     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2009
2010     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2011     {
2012       static TSplitMethod to4methods[4]; // order BT, LR, FB
2013       if ( to4methods[iF]._nbSplits == 0 )
2014       {
2015         switch ( iF ) {
2016         case 0:
2017           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2020           break;
2021         case 1:
2022           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2025           break;
2026         case 2:
2027           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2030           break;
2031         default: return to4methods[3];
2032         }
2033         to4methods[iF]._nbSplits  = 4;
2034         to4methods[iF]._nbCorners = 6;
2035       }
2036       return to4methods[iF];
2037     }
2038     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2039
2040     TSplitMethod method;
2041
2042     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2043
2044     const int nbVariants = 2, nbSplits = 2;
2045     const int** connVariants = 0;
2046     switch ( iF ) {
2047     case 0: connVariants = theHexTo2Prisms_BT; break;
2048     case 1: connVariants = theHexTo2Prisms_LR; break;
2049     case 2: connVariants = theHexTo2Prisms_FB; break;
2050     default: return method;
2051     }
2052
2053     // look for prisms adjacent via facetToSplit and an opposite one
2054     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2055     {
2056       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058       if ( nbNodes != 4 ) return method;
2059
2060       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2063       TTriangleFacet* t;
2064       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2065         t = &t012;
2066       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2067         t = &t123;
2068       else
2069         continue;
2070
2071       // there are adjacent prism
2072       for ( int variant = 0; variant < nbVariants; ++variant )
2073       {
2074         // check method compliancy with adjacent prisms,
2075         // the found prism facets must be among facets of prisms described by current method
2076         method._nbSplits     = nbSplits;
2077         method._nbCorners    = 6;
2078         method._connectivity = connVariants[ variant ];
2079         if ( method.hasFacet( *t ))
2080           return method;
2081       }
2082     }
2083
2084     // No adjacent prisms. Select a variant with a best aspect ratio.
2085
2086     double badness[2] = { 0, 0 };
2087     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088     const SMDS_MeshNode** nodes = vol.GetNodes();
2089     for ( int variant = 0; variant < nbVariants; ++variant )
2090       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2091       {
2092         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2094
2095         method._connectivity = connVariants[ variant ];
2096         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2099
2100         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2101                                 nodes[ t->_n2 ],
2102                                 nodes[ t->_n3 ] );
2103         badness[ variant ] += getBadRate( &tria, aspectRatio );
2104       }
2105     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2106
2107     method._nbSplits     = nbSplits;
2108     method._nbCorners    = 6;
2109     method._connectivity = connVariants[ iBetter ];
2110
2111     return method;
2112   }
2113
2114   //================================================================================
2115   /*!
2116    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2117    */
2118   //================================================================================
2119
2120   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2121                                        const SMDSAbs_GeometryType geom ) const
2122   {
2123     // find the tetrahedron including the three nodes of facet
2124     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128     while ( volIt1->more() )
2129     {
2130       const SMDS_MeshElement* v = volIt1->next();
2131       if ( v->GetGeomType() != geom )
2132         continue;
2133       const int lastCornerInd = v->NbCornerNodes() - 1;
2134       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135         continue; // medium node not allowed
2136       const int ind2 = v->GetNodeIndex( n2 );
2137       if ( ind2 < 0 || lastCornerInd < ind2 )
2138         continue;
2139       const int ind3 = v->GetNodeIndex( n3 );
2140       if ( ind3 < 0 || lastCornerInd < ind3 )
2141         continue;
2142       return true;
2143     }
2144     return false;
2145   }
2146
2147   //=======================================================================
2148   /*!
2149    * \brief A key of a face of volume
2150    */
2151   //=======================================================================
2152
2153   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2154   {
2155     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2156     {
2157       TIDSortedNodeSet sortedNodes;
2158       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159       int nbNodes = vol.NbFaceNodes( iF );
2160       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161       for ( int i = 0; i < nbNodes; i += iQ )
2162         sortedNodes.insert( fNodes[i] );
2163       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164       first.first   = (*(n++))->GetID();
2165       first.second  = (*(n++))->GetID();
2166       second.first  = (*(n++))->GetID();
2167       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2168     }
2169   };
2170 } // namespace
2171
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose  : Split volume elements into tetrahedra or prisms.
2175 //           If facet ID < 0, element is split into tetrahedra,
2176 //           else a hexahedron is split into prisms so that the given facet is
2177 //           split into triangles
2178 //=======================================================================
2179
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181                                      const int            theMethodFlags)
2182 {
2183   SMDS_VolumeTool    volTool;
2184   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185   fHelper.ToFixNodeParameters( true );
2186
2187   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2189
2190   SMESH_SequenceOfElemPtr newNodes, newElems;
2191
2192   // map face of volume to it's baricenrtic node
2193   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2194   double bc[3];
2195
2196   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197   for ( ; elem2facet != theElems.end(); ++elem2facet )
2198   {
2199     const SMDS_MeshElement* elem = elem2facet->first;
2200     const int       facetToSplit = elem2facet->second;
2201     if ( elem->GetType() != SMDSAbs_Volume )
2202       continue;
2203     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2205       continue;
2206
2207     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2208
2209     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2210                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2211                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212     if ( splitMethod._nbSplits < 1 ) continue;
2213
2214     // find submesh to add new tetras to
2215     if ( !subMesh || !subMesh->Contains( elem ))
2216     {
2217       int shapeID = FindShape( elem );
2218       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219       subMesh = GetMeshDS()->MeshElements( shapeID );
2220     }
2221     int iQ;
2222     if ( elem->IsQuadratic() )
2223     {
2224       iQ = 2;
2225       // add quadratic links to the helper
2226       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2227       {
2228         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230         for ( int iN = 0; iN < nbN; iN += iQ )
2231           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2232       }
2233       helper.SetIsQuadratic( true );
2234     }
2235     else
2236     {
2237       iQ = 1;
2238       helper.SetIsQuadratic( false );
2239     }
2240     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241                                         volTool.GetNodes() + elem->NbNodes() );
2242     helper.SetElementsOnShape( true );
2243     if ( splitMethod._baryNode )
2244     {
2245       // make a node at barycenter
2246       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248       nodes.push_back( gcNode );
2249       newNodes.Append( gcNode );
2250     }
2251     if ( !splitMethod._faceBaryNode.empty() )
2252     {
2253       // make or find baricentric nodes of faces
2254       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2256       {
2257         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258           volFace2BaryNode.insert
2259           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2260         if ( !f_n->second )
2261         {
2262           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2264         }
2265         nodes.push_back( iF_n->second = f_n->second );
2266       }
2267     }
2268
2269     // make new volumes
2270     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271     const int* volConn = splitMethod._connectivity;
2272     if ( splitMethod._nbCorners == 4 ) // tetra
2273       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275                                                             nodes[ volConn[1] ],
2276                                                             nodes[ volConn[2] ],
2277                                                             nodes[ volConn[3] ]));
2278     else // prisms
2279       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281                                                             nodes[ volConn[1] ],
2282                                                             nodes[ volConn[2] ],
2283                                                             nodes[ volConn[3] ],
2284                                                             nodes[ volConn[4] ],
2285                                                             nodes[ volConn[5] ]));
2286
2287     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2288
2289     // Split faces on sides of the split volume
2290
2291     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2293     {
2294       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295       if ( nbNodes < 4 ) continue;
2296
2297       // find an existing face
2298       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301                                                                        /*noMedium=*/false))
2302       {
2303         // make triangles
2304         helper.SetElementsOnShape( false );
2305         vector< const SMDS_MeshElement* > triangles;
2306
2307         // find submesh to add new triangles in
2308         if ( !fSubMesh || !fSubMesh->Contains( face ))
2309         {
2310           int shapeID = FindShape( face );
2311           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2312         }
2313         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314         if ( iF_n != splitMethod._faceBaryNode.end() )
2315         {
2316           const SMDS_MeshNode *baryNode = iF_n->second;
2317           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2318           {
2319             const SMDS_MeshNode* n1 = fNodes[iN];
2320             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321             const SMDS_MeshNode *n3 = baryNode;
2322             if ( !volTool.IsFaceExternal( iF ))
2323               swap( n2, n3 );
2324             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2325           }
2326           if ( fSubMesh ) // update position of the bary node on geometry
2327           {
2328             if ( subMesh )
2329               subMesh->RemoveNode( baryNode, false );
2330             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2333             {
2334               fHelper.SetSubShape( s );
2335               gp_XY uv( 1e100, 1e100 );
2336               double distXYZ[4];
2337               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2339                    uv.X() < 1e100 )
2340               {
2341                 // node is too far from the surface
2342                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2345               }
2346             }
2347           }
2348         }
2349         else
2350         {
2351           // among possible triangles create ones discribed by split method
2352           const int* nInd = volTool.GetFaceNodesIndices( iF );
2353           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354           int iCom = 0; // common node of triangle faces to split into
2355           list< TTriangleFacet > facets;
2356           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2357           {
2358             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2359                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2360                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2362                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2363                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2365             {
2366               facets.push_back( t012 );
2367               facets.push_back( t023 );
2368               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2370                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2371                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2372               break;
2373             }
2374           }
2375           list< TTriangleFacet >::iterator facet = facets.begin();
2376           if ( facet == facets.end() )
2377             break;
2378           for ( ; facet != facets.end(); ++facet )
2379           {
2380             if ( !volTool.IsFaceExternal( iF ))
2381               swap( facet->_n2, facet->_n3 );
2382             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383                                                  volNodes[ facet->_n2 ],
2384                                                  volNodes[ facet->_n3 ]));
2385           }
2386         }
2387         for ( int i = 0; i < triangles.size(); ++i )
2388         {
2389           if ( !triangles[i] ) continue;
2390           if ( fSubMesh )
2391             fSubMesh->AddElement( triangles[i]);
2392           newElems.Append( triangles[i] );
2393         }
2394         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2396
2397       } // while a face based on facet nodes exists
2398     } // loop on volume faces to split them into triangles
2399
2400     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2401
2402     if ( geomType == SMDSEntity_TriQuad_Hexa )
2403     {
2404       // remove medium nodes that could become free
2405       for ( int i = 20; i < volTool.NbNodes(); ++i )
2406         if ( volNodes[i]->NbInverseElements() == 0 )
2407           GetMeshDS()->RemoveNode( volNodes[i] );
2408     }
2409   } // loop on volumes to split
2410   
2411   myLastCreatedNodes = newNodes;
2412   myLastCreatedElems = newElems;
2413 }
2414
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose  : For hexahedra that will be split into prisms, finds facets to
2418 //           split into triangles. Only hexahedra adjacent to the one closest
2419 //           to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in]     theFacetNormal - facet normal
2422 //param [out]    theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2424
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426                                              const gp_Ax1&     theFacetNormal,
2427                                              TFacetOfElem &    theFacets)
2428 {
2429   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2430
2431   // Find a hexa closest to the location of theFacetNormal
2432
2433   const SMDS_MeshElement* startHex;
2434   {
2435     // get SMDS_ElemIteratorPtr on theHexas
2436     typedef const SMDS_MeshElement*                                      TValue;
2437     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2438     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2439     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2440     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442       ( new TElemSetIter( theHexas.begin(),
2443                           theHexas.end(),
2444                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2445
2446     SMESH_ElementSearcher* searcher =
2447       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2448
2449     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2450
2451     delete searcher;
2452
2453     if ( !startHex )
2454       throw SALOME_Exception( THIS_METHOD "startHex not found");
2455   }
2456
2457   // Select a facet of startHex by theFacetNormal
2458
2459   SMDS_VolumeTool vTool( startHex );
2460   double norm[3], dot, maxDot = 0;
2461   int facetID = -1;
2462   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2464     {
2465       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2466       if ( dot > maxDot )
2467       {
2468         facetID = iF;
2469         maxDot = dot;
2470       }
2471     }
2472   if ( facetID < 0 )
2473     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2474
2475   // Fill theFacets starting from facetID of startHex
2476
2477   // facets used for seach of volumes adjacent to already treated ones
2478   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2480   TFacetMap facetsToCheck;
2481
2482   set<const SMDS_MeshNode*> facetNodes;
2483   const SMDS_MeshElement*   curHex;
2484
2485   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2486
2487   while ( startHex )
2488   {
2489     // move in two directions from startHex via facetID
2490     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491     {
2492       curHex       = startHex;
2493       int curFacet = facetID;
2494       if ( is2nd ) // do not treat startHex twice
2495       {
2496         vTool.Set( curHex );
2497         if ( vTool.IsFreeFace( curFacet, &curHex ))
2498         {
2499           curHex = 0;
2500         }
2501         else
2502         {
2503           vTool.GetFaceNodes( curFacet, facetNodes );
2504           vTool.Set( curHex );
2505           curFacet = vTool.GetFaceIndex( facetNodes );
2506         }
2507       }
2508       while ( curHex )
2509       {
2510         // store a facet to split
2511         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2512         {
2513           theFacets.insert( make_pair( curHex, -1 ));
2514           break;
2515         }
2516         if ( !allHex && !theHexas.count( curHex ))
2517           break;
2518
2519         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520           theFacets.insert( make_pair( curHex, curFacet ));
2521         if ( !facetIt2isNew.second )
2522           break;
2523
2524         // remember not-to-split facets in facetsToCheck
2525         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2527         {
2528           if ( iF == curFacet && iF == oppFacet )
2529             continue;
2530           TVolumeFaceKey facetKey ( vTool, iF );
2531           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2532           pair< TFacetMap::iterator, bool > it2isnew =
2533             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534           if ( !it2isnew.second )
2535             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2536         }
2537         // pass to a volume adjacent via oppFacet
2538         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2539         {
2540           curHex = 0;
2541         }
2542         else
2543         {
2544           // get a new curFacet
2545           vTool.GetFaceNodes( oppFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2548         }
2549       }
2550     } // move in two directions from startHex via facetID
2551
2552     // Find a new startHex by facetsToCheck
2553
2554     startHex = 0;
2555     facetID  = -1;
2556     TFacetMap::iterator fIt = facetsToCheck.begin();
2557     while ( !startHex && fIt != facetsToCheck.end() )
2558     {
2559       const TElemFacets&  elemFacets = fIt->second;
2560       const SMDS_MeshElement*    hex = elemFacets.first->first;
2561       int                 splitFacet = elemFacets.first->second;
2562       int               lateralFacet = elemFacets.second;
2563       facetsToCheck.erase( fIt );
2564       fIt = facetsToCheck.begin();
2565
2566       vTool.Set( hex );
2567       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2568            curHex->GetGeomType() != SMDSGeom_HEXA )
2569         continue;
2570       if ( !allHex && !theHexas.count( curHex ))
2571         continue;
2572
2573       startHex = curHex;
2574
2575       // find a facet of startHex to split 
2576
2577       set<const SMDS_MeshNode*> lateralNodes;
2578       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579       vTool.GetFaceNodes( splitFacet,   facetNodes );
2580       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581       vTool.Set( startHex );
2582       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2583
2584       // look for a facet of startHex having common nodes with facetNodes
2585       // but not lateralFacet
2586       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2587       {
2588         if ( iF == lateralFacet )
2589           continue;
2590         int nbCommonNodes = 0;
2591         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593           nbCommonNodes += facetNodes.count( nn[ iN ]);
2594
2595         if ( nbCommonNodes >= 2 )
2596         {
2597           facetID = iF;
2598           break;
2599         }
2600       }
2601       if ( facetID < 0 )
2602         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2603     }
2604   } //   while ( startHex )
2605 }
2606
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2611
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613                                         const SMDS_MeshElement* elemInGroups,
2614                                         SMESHDS_Mesh *          aMesh)
2615 {
2616   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617   if (!groups.empty()) {
2618     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619     for ( ; grIt != groups.end(); grIt++ ) {
2620       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621       if ( group && group->Contains( elemInGroups ))
2622         group->SMDSGroup().Add( elemToAdd );
2623     }
2624   }
2625 }
2626
2627
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633                                              SMESHDS_Mesh *          aMesh)
2634 {
2635   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636   if (!groups.empty())
2637   {
2638     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639     for (; GrIt != groups.end(); GrIt++)
2640     {
2641       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642       if (!grp || grp->IsEmpty()) continue;
2643       grp->SMDSGroup().Remove(removeelem);
2644     }
2645   }
2646 }
2647
2648 //================================================================================
2649 /*!
2650  * \brief Replace elemToRm by elemToAdd in the all groups
2651  */
2652 //================================================================================
2653
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655                                             const SMDS_MeshElement* elemToAdd,
2656                                             SMESHDS_Mesh *          aMesh)
2657 {
2658   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659   if (!groups.empty()) {
2660     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661     for ( ; grIt != groups.end(); grIt++ ) {
2662       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664         group->SMDSGroup().Add( elemToAdd );
2665     }
2666   }
2667 }
2668
2669 //================================================================================
2670 /*!
2671  * \brief Replace elemToRm by elemToAdd in the all groups
2672  */
2673 //================================================================================
2674
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2676                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2677                                             SMESHDS_Mesh *                         aMesh)
2678 {
2679   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680   if (!groups.empty())
2681   {
2682     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683     for ( ; grIt != groups.end(); grIt++ ) {
2684       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686         for ( int i = 0; i < elemToAdd.size(); ++i )
2687           group->SMDSGroup().Add( elemToAdd[ i ] );
2688     }
2689   }
2690 }
2691
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose  : Cut quadrangles into triangles.
2695 //           theCrit is used to select a diagonal to cut
2696 //=======================================================================
2697
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699                                   const bool         the13Diag)
2700 {
2701   myLastCreatedElems.Clear();
2702   myLastCreatedNodes.Clear();
2703
2704   MESSAGE( "::QuadToTri()" );
2705
2706   SMESHDS_Mesh * aMesh = GetMeshDS();
2707
2708   Handle(Geom_Surface) surface;
2709   SMESH_MesherHelper   helper( *GetMesh() );
2710
2711   TIDSortedElemSet::iterator itElem;
2712   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713     const SMDS_MeshElement* elem = *itElem;
2714     if ( !elem || elem->GetType() != SMDSAbs_Face )
2715       continue;
2716     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717     if(!isquad) continue;
2718
2719     if(elem->NbNodes()==4) {
2720       // retrieve element nodes
2721       const SMDS_MeshNode* aNodes [4];
2722       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2723       int i = 0;
2724       while ( itN->more() )
2725         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2726
2727       int aShapeId = FindShape( elem );
2728       const SMDS_MeshElement* newElem1 = 0;
2729       const SMDS_MeshElement* newElem2 = 0;
2730       if ( the13Diag ) {
2731         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2733       }
2734       else {
2735         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2737       }
2738       myLastCreatedElems.Append(newElem1);
2739       myLastCreatedElems.Append(newElem2);
2740       // put a new triangle on the same shape and add to the same groups
2741       if ( aShapeId )
2742         {
2743           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745         }
2746       AddToSameGroups( newElem1, elem, aMesh );
2747       AddToSameGroups( newElem2, elem, aMesh );
2748       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749       aMesh->RemoveElement( elem );
2750     }
2751
2752     // Quadratic quadrangle
2753
2754     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2755
2756       // get surface elem is on
2757       int aShapeId = FindShape( elem );
2758       if ( aShapeId != helper.GetSubShapeID() ) {
2759         surface.Nullify();
2760         TopoDS_Shape shape;
2761         if ( aShapeId > 0 )
2762           shape = aMesh->IndexToShape( aShapeId );
2763         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764           TopoDS_Face face = TopoDS::Face( shape );
2765           surface = BRep_Tool::Surface( face );
2766           if ( !surface.IsNull() )
2767             helper.SetSubShape( shape );
2768         }
2769       }
2770
2771       const SMDS_MeshNode* aNodes [8];
2772       const SMDS_MeshNode* inFaceNode = 0;
2773       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2774       int i = 0;
2775       while ( itN->more() ) {
2776         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2779         {
2780           inFaceNode = aNodes[ i-1 ];
2781         }
2782       }
2783
2784       // find middle point for (0,1,2,3)
2785       // and create a node in this point;
2786       gp_XYZ p( 0,0,0 );
2787       if ( surface.IsNull() ) {
2788         for(i=0; i<4; i++)
2789           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2790         p /= 4;
2791       }
2792       else {
2793         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2794         gp_XY uv( 0,0 );
2795         for(i=0; i<4; i++)
2796           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2797         uv /= 4.;
2798         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2799       }
2800       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801       myLastCreatedNodes.Append(newN);
2802
2803       // create a new element
2804       const SMDS_MeshElement* newElem1 = 0;
2805       const SMDS_MeshElement* newElem2 = 0;
2806       if ( the13Diag ) {
2807         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808                                   aNodes[6], aNodes[7], newN );
2809         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810                                   newN,      aNodes[4], aNodes[5] );
2811       }
2812       else {
2813         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814                                   aNodes[7], aNodes[4], newN );
2815         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816                                   newN,      aNodes[5], aNodes[6] );
2817       }
2818       myLastCreatedElems.Append(newElem1);
2819       myLastCreatedElems.Append(newElem2);
2820       // put a new triangle on the same shape and add to the same groups
2821       if ( aShapeId )
2822         {
2823           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2825         }
2826       AddToSameGroups( newElem1, elem, aMesh );
2827       AddToSameGroups( newElem2, elem, aMesh );
2828       aMesh->RemoveElement( elem );
2829     }
2830   }
2831
2832   return true;
2833 }
2834
2835 //=======================================================================
2836 //function : getAngle
2837 //purpose  :
2838 //=======================================================================
2839
2840 double getAngle(const SMDS_MeshElement * tr1,
2841                 const SMDS_MeshElement * tr2,
2842                 const SMDS_MeshNode *    n1,
2843                 const SMDS_MeshNode *    n2)
2844 {
2845   double angle = 2. * M_PI; // bad angle
2846
2847   // get normals
2848   SMESH::Controls::TSequenceOfXYZ P1, P2;
2849   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2851     return angle;
2852   gp_Vec N1,N2;
2853   if(!tr1->IsQuadratic())
2854     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2855   else
2856     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857   if ( N1.SquareMagnitude() <= gp::Resolution() )
2858     return angle;
2859   if(!tr2->IsQuadratic())
2860     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2861   else
2862     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863   if ( N2.SquareMagnitude() <= gp::Resolution() )
2864     return angle;
2865
2866   // find the first diagonal node n1 in the triangles:
2867   // take in account a diagonal link orientation
2868   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869   for ( int t = 0; t < 2; t++ ) {
2870     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871     int i = 0, iDiag = -1;
2872     while ( it->more()) {
2873       const SMDS_MeshElement *n = it->next();
2874       if ( n == n1 || n == n2 ) {
2875         if ( iDiag < 0)
2876           iDiag = i;
2877         else {
2878           if ( i - iDiag == 1 )
2879             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2880           else
2881             nFirst[ t ] = n;
2882           break;
2883         }
2884       }
2885       i++;
2886     }
2887   }
2888   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2889     N2.Reverse();
2890
2891   angle = N1.Angle( N2 );
2892   //SCRUTE( angle );
2893   return angle;
2894 }
2895
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2900 class LinkID_Gen {
2901 public:
2902
2903   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2905   {}
2906
2907   long GetLinkID (const SMDS_MeshNode * n1,
2908                   const SMDS_MeshNode * n2) const
2909   {
2910     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2911   }
2912
2913   bool GetNodes (const long             theLinkID,
2914                  const SMDS_MeshNode* & theNode1,
2915                  const SMDS_MeshNode* & theNode2) const
2916   {
2917     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918     if ( !theNode1 ) return false;
2919     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920     if ( !theNode2 ) return false;
2921     return true;
2922   }
2923
2924 private:
2925   LinkID_Gen();
2926   const SMESHDS_Mesh* myMesh;
2927   long                myMaxID;
2928 };
2929
2930
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose  : Fuse neighbour triangles into quadrangles.
2934 //           theCrit is used to select a neighbour to fuse with.
2935 //           theMaxAngle is a max angle between element normals at which
2936 //           fusion is still performed.
2937 //=======================================================================
2938
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2940                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2941                                   const double                         theMaxAngle)
2942 {
2943   myLastCreatedElems.Clear();
2944   myLastCreatedNodes.Clear();
2945
2946   MESSAGE( "::TriToQuad()" );
2947
2948   if ( !theCrit.get() )
2949     return false;
2950
2951   SMESHDS_Mesh * aMesh = GetMeshDS();
2952
2953   // Prepare data for algo: build
2954   // 1. map of elements with their linkIDs
2955   // 2. map of linkIDs with their elements
2956
2957   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2960   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2961
2962   TIDSortedElemSet::iterator itElem;
2963   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964   {
2965     const SMDS_MeshElement* elem = *itElem;
2966     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967     bool IsTria = ( elem->NbCornerNodes()==3 );
2968     if (!IsTria) continue;
2969
2970     // retrieve element nodes
2971     const SMDS_MeshNode* aNodes [4];
2972     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2973     int i = 0;
2974     while ( i < 3 )
2975       aNodes[ i++ ] = itN->next();
2976     aNodes[ 3 ] = aNodes[ 0 ];
2977
2978     // fill maps
2979     for ( i = 0; i < 3; i++ ) {
2980       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981       // check if elements sharing a link can be fused
2982       itLE = mapLi_listEl.find( link );
2983       if ( itLE != mapLi_listEl.end() ) {
2984         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2985           continue;
2986         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987         //if ( FindShape( elem ) != FindShape( elem2 ))
2988         //  continue; // do not fuse triangles laying on different shapes
2989         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990           continue; // avoid making badly shaped quads
2991         (*itLE).second.push_back( elem );
2992       }
2993       else {
2994         mapLi_listEl[ link ].push_back( elem );
2995       }
2996       mapEl_setLi [ elem ].insert( link );
2997     }
2998   }
2999   // Clean the maps from the links shared by a sole element, ie
3000   // links to which only one element is bound in mapLi_listEl
3001
3002   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003     int nbElems = (*itLE).second.size();
3004     if ( nbElems < 2  ) {
3005       const SMDS_MeshElement* elem = (*itLE).second.front();
3006       SMESH_TLink link = (*itLE).first;
3007       mapEl_setLi[ elem ].erase( link );
3008       if ( mapEl_setLi[ elem ].empty() )
3009         mapEl_setLi.erase( elem );
3010     }
3011   }
3012
3013   // Algo: fuse triangles into quadrangles
3014
3015   while ( ! mapEl_setLi.empty() ) {
3016     // Look for the start element:
3017     // the element having the least nb of shared links
3018     const SMDS_MeshElement* startElem = 0;
3019     int minNbLinks = 4;
3020     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021       int nbLinks = (*itEL).second.size();
3022       if ( nbLinks < minNbLinks ) {
3023         startElem = (*itEL).first;
3024         minNbLinks = nbLinks;
3025         if ( minNbLinks == 1 )
3026           break;
3027       }
3028     }
3029
3030     // search elements to fuse starting from startElem or links of elements
3031     // fused earlyer - startLinks
3032     list< SMESH_TLink > startLinks;
3033     while ( startElem || !startLinks.empty() ) {
3034       while ( !startElem && !startLinks.empty() ) {
3035         // Get an element to start, by a link
3036         SMESH_TLink linkId = startLinks.front();
3037         startLinks.pop_front();
3038         itLE = mapLi_listEl.find( linkId );
3039         if ( itLE != mapLi_listEl.end() ) {
3040           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042           for ( ; itE != listElem.end() ; itE++ )
3043             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3044               startElem = (*itE);
3045           mapLi_listEl.erase( itLE );
3046         }
3047       }
3048
3049       if ( startElem ) {
3050         // Get candidates to be fused
3051         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052         const SMESH_TLink *link12, *link13;
3053         startElem = 0;
3054         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056         ASSERT( !setLi.empty() );
3057         set< SMESH_TLink >::iterator itLi;
3058         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3059         {
3060           const SMESH_TLink & link = (*itLi);
3061           itLE = mapLi_listEl.find( link );
3062           if ( itLE == mapLi_listEl.end() )
3063             continue;
3064
3065           const SMDS_MeshElement* elem = (*itLE).second.front();
3066           if ( elem == tr1 )
3067             elem = (*itLE).second.back();
3068           mapLi_listEl.erase( itLE );
3069           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3070             continue;
3071           if ( tr2 ) {
3072             tr3 = elem;
3073             link13 = &link;
3074           }
3075           else {
3076             tr2 = elem;
3077             link12 = &link;
3078           }
3079
3080           // add other links of elem to list of links to re-start from
3081           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082           set< SMESH_TLink >::iterator it;
3083           for ( it = links.begin(); it != links.end(); it++ ) {
3084             const SMESH_TLink& link2 = (*it);
3085             if ( link2 != link )
3086               startLinks.push_back( link2 );
3087           }
3088         }
3089
3090         // Get nodes of possible quadrangles
3091         const SMDS_MeshNode *n12 [4], *n13 [4];
3092         bool Ok12 = false, Ok13 = false;
3093         const SMDS_MeshNode *linkNode1, *linkNode2;
3094         if(tr2) {
3095           linkNode1 = link12->first;
3096           linkNode2 = link12->second;
3097           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3098             Ok12 = true;
3099         }
3100         if(tr3) {
3101           linkNode1 = link13->first;
3102           linkNode2 = link13->second;
3103           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3104             Ok13 = true;
3105         }
3106
3107         // Choose a pair to fuse
3108         if ( Ok12 && Ok13 ) {
3109           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111           double aBadRate12 = getBadRate( &quad12, theCrit );
3112           double aBadRate13 = getBadRate( &quad13, theCrit );
3113           if (  aBadRate13 < aBadRate12 )
3114             Ok12 = false;
3115           else
3116             Ok13 = false;
3117         }
3118
3119         // Make quadrangles
3120         // and remove fused elems and remove links from the maps
3121         mapEl_setLi.erase( tr1 );
3122         if ( Ok12 )
3123         {
3124           mapEl_setLi.erase( tr2 );
3125           mapLi_listEl.erase( *link12 );
3126           if ( tr1->NbNodes() == 3 )
3127           {
3128             const SMDS_MeshElement* newElem = 0;
3129             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130             myLastCreatedElems.Append(newElem);
3131             AddToSameGroups( newElem, tr1, aMesh );
3132             int aShapeId = tr1->getshapeId();
3133             if ( aShapeId )
3134               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135             aMesh->RemoveElement( tr1 );
3136             aMesh->RemoveElement( tr2 );
3137           }
3138           else {
3139             vector< const SMDS_MeshNode* > N1;
3140             vector< const SMDS_MeshNode* > N2;
3141             getNodesFromTwoTria(tr1,tr2,N1,N2);
3142             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3144             // i.e. first nodes from both arrays form a new diagonal
3145             const SMDS_MeshNode* aNodes[8];
3146             aNodes[0] = N1[0];
3147             aNodes[1] = N1[1];
3148             aNodes[2] = N2[0];
3149             aNodes[3] = N2[1];
3150             aNodes[4] = N1[3];
3151             aNodes[5] = N2[5];
3152             aNodes[6] = N2[3];
3153             aNodes[7] = N1[5];
3154             const SMDS_MeshElement* newElem = 0;
3155             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3158             else
3159               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161             myLastCreatedElems.Append(newElem);
3162             AddToSameGroups( newElem, tr1, aMesh );
3163             int aShapeId = tr1->getshapeId();
3164             if ( aShapeId )
3165               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166             aMesh->RemoveElement( tr1 );
3167             aMesh->RemoveElement( tr2 );
3168             // remove middle node (9)
3169             if ( N1[4]->NbInverseElements() == 0 )
3170               aMesh->RemoveNode( N1[4] );
3171             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172               aMesh->RemoveNode( N1[6] );
3173             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174               aMesh->RemoveNode( N2[6] );
3175           }
3176         }
3177         else if ( Ok13 )
3178         {
3179           mapEl_setLi.erase( tr3 );
3180           mapLi_listEl.erase( *link13 );
3181           if ( tr1->NbNodes() == 3 ) {
3182             const SMDS_MeshElement* newElem = 0;
3183             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184             myLastCreatedElems.Append(newElem);
3185             AddToSameGroups( newElem, tr1, aMesh );
3186             int aShapeId = tr1->getshapeId();
3187             if ( aShapeId )
3188               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189             aMesh->RemoveElement( tr1 );
3190             aMesh->RemoveElement( tr3 );
3191           }
3192           else {
3193             vector< const SMDS_MeshNode* > N1;
3194             vector< const SMDS_MeshNode* > N2;
3195             getNodesFromTwoTria(tr1,tr3,N1,N2);
3196             // now we receive following N1 and N2 (using numeration as above image)
3197             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3198             // i.e. first nodes from both arrays form a new diagonal
3199             const SMDS_MeshNode* aNodes[8];
3200             aNodes[0] = N1[0];
3201             aNodes[1] = N1[1];
3202             aNodes[2] = N2[0];
3203             aNodes[3] = N2[1];
3204             aNodes[4] = N1[3];
3205             aNodes[5] = N2[5];
3206             aNodes[6] = N2[3];
3207             aNodes[7] = N1[5];
3208             const SMDS_MeshElement* newElem = 0;
3209             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3212             else
3213               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215             myLastCreatedElems.Append(newElem);
3216             AddToSameGroups( newElem, tr1, aMesh );
3217             int aShapeId = tr1->getshapeId();
3218             if ( aShapeId )
3219               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220             aMesh->RemoveElement( tr1 );
3221             aMesh->RemoveElement( tr3 );
3222             // remove middle node (9)
3223             if ( N1[4]->NbInverseElements() == 0 )
3224               aMesh->RemoveNode( N1[4] );
3225             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226               aMesh->RemoveNode( N1[6] );
3227             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228               aMesh->RemoveNode( N2[6] );
3229           }
3230         }
3231
3232         // Next element to fuse: the rejected one
3233         if ( tr3 )
3234           startElem = Ok12 ? tr3 : tr2;
3235
3236       } // if ( startElem )
3237     } // while ( startElem || !startLinks.empty() )
3238   } // while ( ! mapEl_setLi.empty() )
3239
3240   return true;
3241 }
3242
3243
3244 /*#define DUMPSO(txt) \
3245 //  cout << txt << endl;
3246 //=============================================================================
3247 //
3248 //
3249 //
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3252 {
3253 if ( i1 == i2 )
3254 return;
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3259 P[ i1 ] = P[ i2 ];
3260 P[ i2 ] = Ptmp;
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3262 }
3263
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3267 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3268 //           1 or 2 else 0.
3269 //=======================================================================
3270
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3272 int               idNodes[] )
3273 {
3274   gp_Pnt P[4];
3275   int i;
3276   for ( i = 0; i < 4; i++ ) {
3277     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3278     if ( !n ) return 0;
3279     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3280   }
3281
3282   gp_Vec V1(P[0], P[1]);
3283   gp_Vec V2(P[0], P[2]);
3284   gp_Vec V3(P[0], P[3]);
3285
3286   gp_Vec Cross1 = V1 ^ V2;
3287   gp_Vec Cross2 = V2 ^ V3;
3288
3289   i = 0;
3290   if (Cross1.Dot(Cross2) < 0)
3291   {
3292     Cross1 = V2 ^ V1;
3293     Cross2 = V1 ^ V3;
3294
3295     if (Cross1.Dot(Cross2) < 0)
3296       i = 2;
3297     else
3298       i = 1;
3299     swap ( i, i + 1, idNodes, P );
3300
3301     //     for ( int ii = 0; ii < 4; ii++ ) {
3302     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3304     //     }
3305   }
3306   return i;
3307 }
3308
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose  : Set 8 nodes of a hexahedron in a good order.
3312 //           Return success status
3313 //=======================================================================
3314
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3316                                       int               idNodes[] )
3317 {
3318   gp_Pnt P[8];
3319   int i;
3320   DUMPSO( "INPUT: ========================================");
3321   for ( i = 0; i < 8; i++ ) {
3322     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323     if ( !n ) return false;
3324     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3326   }
3327   DUMPSO( "========================================");
3328
3329
3330   set<int> faceNodes;  // ids of bottom face nodes, to be found
3331   set<int> checkedId1; // ids of tried 2-nd nodes
3332   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3334   int iMin, iLoop1 = 0;
3335
3336   // Loop to try the 2-nd nodes
3337
3338   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3339   {
3340     // Find not checked 2-nd node
3341     for ( i = 1; i < 8; i++ )
3342       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343         int id1 = idNodes[i];
3344         swap ( 1, i, idNodes, P );
3345         checkedId1.insert ( id1 );
3346         break;
3347       }
3348
3349     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350     // ie that all but meybe one (id3 which is on the same face) nodes
3351     // lay on the same side from the triangle plane.
3352
3353     bool manyInPlane = false; // more than 4 nodes lay in plane
3354     int iLoop2 = 0;
3355     while ( ++iLoop2 < 6 ) {
3356
3357       // get 1-2-3 plane coeffs
3358       Standard_Real A, B, C, D;
3359       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360       if ( N.SquareMagnitude() > gp::Resolution() )
3361       {
3362         gp_Pln pln ( P[0], N );
3363         pln.Coefficients( A, B, C, D );
3364
3365         // find the node (iMin) closest to pln
3366         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3367         set<int> idInPln;
3368         for ( i = 3; i < 8; i++ ) {
3369           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370           if ( fabs( dist[i] ) < minDist ) {
3371             minDist = fabs( dist[i] );
3372             iMin = i;
3373           }
3374           if ( fabs( dist[i] ) <= tol )
3375             idInPln.insert( idNodes[i] );
3376         }
3377
3378         // there should not be more than 4 nodes in bottom plane
3379         if ( idInPln.size() > 1 )
3380         {
3381           DUMPSO( "### idInPln.size() = " << idInPln.size());
3382           // idInPlane does not contain the first 3 nodes
3383           if ( manyInPlane || idInPln.size() == 5)
3384             return false; // all nodes in one plane
3385           manyInPlane = true;
3386
3387           // set the 1-st node to be not in plane
3388           for ( i = 3; i < 8; i++ ) {
3389             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390               DUMPSO( "### Reset 0-th node");
3391               swap( 0, i, idNodes, P );
3392               break;
3393             }
3394           }
3395
3396           // reset to re-check second nodes
3397           leastDist = DBL_MAX;
3398           faceNodes.clear();
3399           checkedId1.clear();
3400           iLoop1 = 0;
3401           break; // from iLoop2;
3402         }
3403
3404         // check that the other 4 nodes are on the same side
3405         bool sameSide = true;
3406         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407         for ( i = 3; sameSide && i < 8; i++ ) {
3408           if ( i != iMin )
3409             sameSide = ( isNeg == dist[i] <= 0.);
3410         }
3411
3412         // keep best solution
3413         if ( sameSide && minDist < leastDist ) {
3414           leastDist = minDist;
3415           faceNodes.clear();
3416           faceNodes.insert( idNodes[ 1 ] );
3417           faceNodes.insert( idNodes[ 2 ] );
3418           faceNodes.insert( idNodes[ iMin ] );
3419           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420                   << " leastDist = " << leastDist);
3421           if ( leastDist <= DBL_MIN )
3422             break;
3423         }
3424       }
3425
3426       // set next 3-d node to check
3427       int iNext = 2 + iLoop2;
3428       if ( iNext < 8 ) {
3429         DUMPSO( "Try 2-nd");
3430         swap ( 2, iNext, idNodes, P );
3431       }
3432     } // while ( iLoop2 < 6 )
3433   } // iLoop1
3434
3435   if ( faceNodes.empty() ) return false;
3436
3437   // Put the faceNodes in proper places
3438   for ( i = 4; i < 8; i++ ) {
3439     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440       // find a place to put
3441       int iTo = 1;
3442       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3443         iTo++;
3444       DUMPSO( "Set faceNodes");
3445       swap ( iTo, i, idNodes, P );
3446     }
3447   }
3448
3449
3450   // Set nodes of the found bottom face in good order
3451   DUMPSO( " Found bottom face: ");
3452   i = SortQuadNodes( theMesh, idNodes );
3453   if ( i ) {
3454     gp_Pnt Ptmp = P[ i ];
3455     P[ i ] = P[ i+1 ];
3456     P[ i+1 ] = Ptmp;
3457   }
3458   //   else
3459   //     for ( int ii = 0; ii < 4; ii++ ) {
3460   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3462   //    }
3463
3464   // Gravity center of the top and bottom faces
3465   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3467
3468   // Get direction from the bottom to the top face
3469   gp_Vec upDir ( aGCb, aGCt );
3470   Standard_Real upDirSize = upDir.Magnitude();
3471   if ( upDirSize <= gp::Resolution() ) return false;
3472   upDir / upDirSize;
3473
3474   // Assure that the bottom face normal points up
3475   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477   if ( Nb.Dot( upDir ) < 0 ) {
3478     DUMPSO( "Reverse bottom face");
3479     swap( 1, 3, idNodes, P );
3480   }
3481
3482   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483   Standard_Real minDist = DBL_MAX;
3484   for ( i = 4; i < 8; i++ ) {
3485     // projection of P[i] to the plane defined by P[0] and upDir
3486     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487     Standard_Real sqDist = P[0].SquareDistance( Pp );
3488     if ( sqDist < minDist ) {
3489       minDist = sqDist;
3490       iMin = i;
3491     }
3492   }
3493   DUMPSO( "Set 4-th");
3494   swap ( 4, iMin, idNodes, P );
3495
3496   // Set nodes of the top face in good order
3497   DUMPSO( "Sort top face");
3498   i = SortQuadNodes( theMesh, &idNodes[4] );
3499   if ( i ) {
3500     i += 4;
3501     gp_Pnt Ptmp = P[ i ];
3502     P[ i ] = P[ i+1 ];
3503     P[ i+1 ] = Ptmp;
3504   }
3505
3506   // Assure that direction of the top face normal is from the bottom face
3507   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509   if ( Nt.Dot( upDir ) < 0 ) {
3510     DUMPSO( "Reverse top face");
3511     swap( 5, 7, idNodes, P );
3512   }
3513
3514   //   DUMPSO( "OUTPUT: ========================================");
3515   //   for ( i = 0; i < 8; i++ ) {
3516   //     float *p = ugrid->GetPoint(idNodes[i]);
3517   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3518   //   }
3519
3520   return true;
3521 }*/
3522
3523 //================================================================================
3524 /*!
3525  * \brief Return nodes linked to the given one
3526  * \param theNode - the node
3527  * \param linkedNodes - the found nodes
3528  * \param type - the type of elements to check
3529  *
3530  * Medium nodes are ignored
3531  */
3532 //================================================================================
3533
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535                                        TIDSortedElemSet &   linkedNodes,
3536                                        SMDSAbs_ElementType  type )
3537 {
3538   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539   while ( elemIt->more() )
3540   {
3541     const SMDS_MeshElement* elem = elemIt->next();
3542     if(elem->GetType() == SMDSAbs_0DElement)
3543       continue;
3544
3545     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546     if ( elem->GetType() == SMDSAbs_Volume )
3547     {
3548       SMDS_VolumeTool vol( elem );
3549       while ( nodeIt->more() ) {
3550         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551         if ( theNode != n && vol.IsLinked( theNode, n ))
3552           linkedNodes.insert( n );
3553       }
3554     }
3555     else
3556     {
3557       for ( int i = 0; nodeIt->more(); ++i ) {
3558         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559         if ( n == theNode ) {
3560           int iBefore = i - 1;
3561           int iAfter  = i + 1;
3562           if ( elem->IsQuadratic() ) {
3563             int nb = elem->NbNodes() / 2;
3564             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3566           }
3567           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3569         }
3570       }
3571     }
3572   }
3573 }
3574
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose  : pulls theNode toward the center of surrounding nodes directly
3578 //           connected to that node along an element edge
3579 //=======================================================================
3580
3581 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3582                      const Handle(Geom_Surface)&          theSurface,
3583                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3584 {
3585   // find surrounding nodes
3586
3587   TIDSortedElemSet nodeSet;
3588   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3589
3590   // compute new coodrs
3591
3592   double coord[] = { 0., 0., 0. };
3593   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596     if ( theSurface.IsNull() ) { // smooth in 3D
3597       coord[0] += node->X();
3598       coord[1] += node->Y();
3599       coord[2] += node->Z();
3600     }
3601     else { // smooth in 2D
3602       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603       gp_XY* uv = theUVMap[ node ];
3604       coord[0] += uv->X();
3605       coord[1] += uv->Y();
3606     }
3607   }
3608   int nbNodes = nodeSet.size();
3609   if ( !nbNodes )
3610     return;
3611   coord[0] /= nbNodes;
3612   coord[1] /= nbNodes;
3613
3614   if ( !theSurface.IsNull() ) {
3615     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3618     coord[0] = p3d.X();
3619     coord[1] = p3d.Y();
3620     coord[2] = p3d.Z();
3621   }
3622   else
3623     coord[2] /= nbNodes;
3624
3625   // move node
3626
3627   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3628 }
3629
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3633 //           surrounding elements
3634 //=======================================================================
3635
3636 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3637                       const Handle(Geom_Surface)&          theSurface,
3638                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3639 {
3640   gp_XYZ aNewXYZ(0.,0.,0.);
3641   SMESH::Controls::Area anAreaFunc;
3642   double totalArea = 0.;
3643   int nbElems = 0;
3644
3645   // compute new XYZ
3646
3647   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648   while ( elemIt->more() )
3649   {
3650     const SMDS_MeshElement* elem = elemIt->next();
3651     nbElems++;
3652
3653     gp_XYZ elemCenter(0.,0.,0.);
3654     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656     int nn = elem->NbNodes();
3657     if(elem->IsQuadratic()) nn = nn/2;
3658     int i=0;
3659     //while ( itN->more() ) {
3660     while ( i<nn ) {
3661       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3662       i++;
3663       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664       aNodePoints.push_back( aP );
3665       if ( !theSurface.IsNull() ) { // smooth in 2D
3666         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667         gp_XY* uv = theUVMap[ aNode ];
3668         aP.SetCoord( uv->X(), uv->Y(), 0. );
3669       }
3670       elemCenter += aP;
3671     }
3672     double elemArea = anAreaFunc.GetValue( aNodePoints );
3673     totalArea += elemArea;
3674     elemCenter /= nn;
3675     aNewXYZ += elemCenter * elemArea;
3676   }
3677   aNewXYZ /= totalArea;
3678   if ( !theSurface.IsNull() ) {
3679     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3681   }
3682
3683   // move node
3684
3685   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3686 }
3687
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose  : return UV of closest projection
3691 //=======================================================================
3692
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694                           const gp_Pnt&     point,
3695                           gp_XY &           result)
3696 {
3697   projector.Perform( point );
3698   if ( projector.IsDone() ) {
3699     double u, v, minVal = DBL_MAX;
3700     for ( int i = projector.NbExt(); i > 0; i-- )
3701       if ( projector.SquareDistance( i ) < minVal ) {
3702         minVal = projector.SquareDistance( i );
3703         projector.Point( i ).Parameter( u, v );
3704       }
3705     result.SetCoord( u, v );
3706     return true;
3707   }
3708   return false;
3709 }
3710
3711 //=======================================================================
3712 //function : Smooth
3713 //purpose  : Smooth theElements during theNbIterations or until a worst
3714 //           element has aspect ratio <= theTgtAspectRatio.
3715 //           Aspect Ratio varies in range [1.0, inf].
3716 //           If theElements is empty, the whole mesh is smoothed.
3717 //           theFixedNodes contains additionally fixed nodes. Nodes built
3718 //           on edges and boundary nodes are always fixed.
3719 //=======================================================================
3720
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3722                                set<const SMDS_MeshNode*> & theFixedNodes,
3723                                const SmoothMethod          theSmoothMethod,
3724                                const int                   theNbIterations,
3725                                double                      theTgtAspectRatio,
3726                                const bool                  the2D)
3727 {
3728   myLastCreatedElems.Clear();
3729   myLastCreatedNodes.Clear();
3730
3731   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3732
3733   if ( theTgtAspectRatio < 1.0 )
3734     theTgtAspectRatio = 1.0;
3735
3736   const double disttol = 1.e-16;
3737
3738   SMESH::Controls::AspectRatio aQualityFunc;
3739
3740   SMESHDS_Mesh* aMesh = GetMeshDS();
3741
3742   if ( theElems.empty() ) {
3743     // add all faces to theElems
3744     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745     while ( fIt->more() ) {
3746       const SMDS_MeshElement* face = fIt->next();
3747       theElems.insert( theElems.end(), face );
3748     }
3749   }
3750   // get all face ids theElems are on
3751   set< int > faceIdSet;
3752   TIDSortedElemSet::iterator itElem;
3753   if ( the2D )
3754     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755       int fId = FindShape( *itElem );
3756       // check that corresponding submesh exists and a shape is face
3757       if (fId &&
3758           faceIdSet.find( fId ) == faceIdSet.end() &&
3759           aMesh->MeshElements( fId )) {
3760         TopoDS_Shape F = aMesh->IndexToShape( fId );
3761         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762           faceIdSet.insert( fId );
3763       }
3764     }
3765   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3766
3767   // ===============================================
3768   // smooth elements on each TopoDS_Face separately
3769   // ===============================================
3770
3771   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3772   for ( ; fId != faceIdSet.rend(); ++fId ) {
3773     // get face surface and submesh
3774     Handle(Geom_Surface) surface;
3775     SMESHDS_SubMesh* faceSubMesh = 0;
3776     TopoDS_Face face;
3777     double fToler2 = 0, f,l;
3778     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3779     bool isUPeriodic = false, isVPeriodic = false;
3780     if ( *fId ) {
3781       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3782       surface = BRep_Tool::Surface( face );
3783       faceSubMesh = aMesh->MeshElements( *fId );
3784       fToler2 = BRep_Tool::Tolerance( face );
3785       fToler2 *= fToler2 * 10.;
3786       isUPeriodic = surface->IsUPeriodic();
3787       if ( isUPeriodic )
3788         surface->UPeriod();
3789       isVPeriodic = surface->IsVPeriodic();
3790       if ( isVPeriodic )
3791         surface->VPeriod();
3792       surface->Bounds( u1, u2, v1, v2 );
3793     }
3794     // ---------------------------------------------------------
3795     // for elements on a face, find movable and fixed nodes and
3796     // compute UV for them
3797     // ---------------------------------------------------------
3798     bool checkBoundaryNodes = false;
3799     bool isQuadratic = false;
3800     set<const SMDS_MeshNode*> setMovableNodes;
3801     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3802     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3803     list< const SMDS_MeshElement* > elemsOnFace;
3804
3805     Extrema_GenExtPS projector;
3806     GeomAdaptor_Surface surfAdaptor;
3807     if ( !surface.IsNull() ) {
3808       surfAdaptor.Load( surface );
3809       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3810     }
3811     int nbElemOnFace = 0;
3812     itElem = theElems.begin();
3813     // loop on not yet smoothed elements: look for elems on a face
3814     while ( itElem != theElems.end() ) {
3815       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3816         break; // all elements found
3817
3818       const SMDS_MeshElement* elem = *itElem;
3819       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3820            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3821         ++itElem;
3822         continue;
3823       }
3824       elemsOnFace.push_back( elem );
3825       theElems.erase( itElem++ );
3826       nbElemOnFace++;
3827
3828       if ( !isQuadratic )
3829         isQuadratic = elem->IsQuadratic();
3830
3831       // get movable nodes of elem
3832       const SMDS_MeshNode* node;
3833       SMDS_TypeOfPosition posType;
3834       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3835       int nn = 0, nbn =  elem->NbNodes();
3836       if(elem->IsQuadratic())
3837         nbn = nbn/2;
3838       while ( nn++ < nbn ) {
3839         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3840         const SMDS_PositionPtr& pos = node->GetPosition();
3841         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842         if (posType != SMDS_TOP_EDGE &&
3843             posType != SMDS_TOP_VERTEX &&
3844             theFixedNodes.find( node ) == theFixedNodes.end())
3845         {
3846           // check if all faces around the node are on faceSubMesh
3847           // because a node on edge may be bound to face
3848           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3849           bool all = true;
3850           if ( faceSubMesh ) {
3851             while ( eIt->more() && all ) {
3852               const SMDS_MeshElement* e = eIt->next();
3853               all = faceSubMesh->Contains( e );
3854             }
3855           }
3856           if ( all )
3857             setMovableNodes.insert( node );
3858           else
3859             checkBoundaryNodes = true;
3860         }
3861         if ( posType == SMDS_TOP_3DSPACE )
3862           checkBoundaryNodes = true;
3863       }
3864
3865       if ( surface.IsNull() )
3866         continue;
3867
3868       // get nodes to check UV
3869       list< const SMDS_MeshNode* > uvCheckNodes;
3870       itN = elem->nodesIterator();
3871       nn = 0; nbn =  elem->NbNodes();
3872       if(elem->IsQuadratic())
3873         nbn = nbn/2;
3874       while ( nn++ < nbn ) {
3875         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876         if ( uvMap.find( node ) == uvMap.end() )
3877           uvCheckNodes.push_back( node );
3878         // add nodes of elems sharing node
3879         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3880         //         while ( eIt->more() ) {
3881         //           const SMDS_MeshElement* e = eIt->next();
3882         //           if ( e != elem ) {
3883         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3884         //             while ( nIt->more() ) {
3885         //               const SMDS_MeshNode* n =
3886         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3887         //               if ( uvMap.find( n ) == uvMap.end() )
3888         //                 uvCheckNodes.push_back( n );
3889         //             }
3890         //           }
3891         //         }
3892       }
3893       // check UV on face
3894       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3895       for ( ; n != uvCheckNodes.end(); ++n ) {
3896         node = *n;
3897         gp_XY uv( 0, 0 );
3898         const SMDS_PositionPtr& pos = node->GetPosition();
3899         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3900         // get existing UV
3901         switch ( posType ) {
3902         case SMDS_TOP_FACE: {
3903           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3904           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3905           break;
3906         }
3907         case SMDS_TOP_EDGE: {
3908           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3909           Handle(Geom2d_Curve) pcurve;
3910           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3911             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3912           if ( !pcurve.IsNull() ) {
3913             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3914             uv = pcurve->Value( u ).XY();
3915           }
3916           break;
3917         }
3918         case SMDS_TOP_VERTEX: {
3919           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3920           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3921             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3922           break;
3923         }
3924         default:;
3925         }
3926         // check existing UV
3927         bool project = true;
3928         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3929         double dist1 = DBL_MAX, dist2 = 0;
3930         if ( posType != SMDS_TOP_3DSPACE ) {
3931           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3932           project = dist1 > fToler2;
3933         }
3934         if ( project ) { // compute new UV
3935           gp_XY newUV;
3936           if ( !getClosestUV( projector, pNode, newUV )) {
3937             MESSAGE("Node Projection Failed " << node);
3938           }
3939           else {
3940             if ( isUPeriodic )
3941               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3942             if ( isVPeriodic )
3943               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3944             // check new UV
3945             if ( posType != SMDS_TOP_3DSPACE )
3946               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3947             if ( dist2 < dist1 )
3948               uv = newUV;
3949           }
3950         }
3951         // store UV in the map
3952         listUV.push_back( uv );
3953         uvMap.insert( make_pair( node, &listUV.back() ));
3954       }
3955     } // loop on not yet smoothed elements
3956
3957     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3958       checkBoundaryNodes = true;
3959
3960     // fix nodes on mesh boundary
3961
3962     if ( checkBoundaryNodes ) {
3963       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3964       map< SMESH_TLink, int >::iterator link_nb;
3965       // put all elements links to linkNbMap
3966       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3967       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3968         const SMDS_MeshElement* elem = (*elemIt);
3969         int nbn =  elem->NbCornerNodes();
3970         // loop on elem links: insert them in linkNbMap
3971         for ( int iN = 0; iN < nbn; ++iN ) {
3972           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3973           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3974           SMESH_TLink link( n1, n2 );
3975           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3976           link_nb->second++;
3977         }
3978       }
3979       // remove nodes that are in links encountered only once from setMovableNodes
3980       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3981         if ( link_nb->second == 1 ) {
3982           setMovableNodes.erase( link_nb->first.node1() );
3983           setMovableNodes.erase( link_nb->first.node2() );
3984         }
3985       }
3986     }
3987
3988     // -----------------------------------------------------
3989     // for nodes on seam edge, compute one more UV ( uvMap2 );
3990     // find movable nodes linked to nodes on seam and which
3991     // are to be smoothed using the second UV ( uvMap2 )
3992     // -----------------------------------------------------
3993
3994     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3995     if ( !surface.IsNull() ) {
3996       TopExp_Explorer eExp( face, TopAbs_EDGE );
3997       for ( ; eExp.More(); eExp.Next() ) {
3998         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3999         if ( !BRep_Tool::IsClosed( edge, face ))
4000           continue;
4001         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4002         if ( !sm ) continue;
4003         // find out which parameter varies for a node on seam
4004         double f,l;
4005         gp_Pnt2d uv1, uv2;
4006         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4007         if ( pcurve.IsNull() ) continue;
4008         uv1 = pcurve->Value( f );
4009         edge.Reverse();
4010         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011         if ( pcurve.IsNull() ) continue;
4012         uv2 = pcurve->Value( f );
4013         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4014         // assure uv1 < uv2
4015         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4016           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4017         }
4018         // get nodes on seam and its vertices
4019         list< const SMDS_MeshNode* > seamNodes;
4020         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4021         while ( nSeamIt->more() ) {
4022           const SMDS_MeshNode* node = nSeamIt->next();
4023           if ( !isQuadratic || !IsMedium( node ))
4024             seamNodes.push_back( node );
4025         }
4026         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4027         for ( ; vExp.More(); vExp.Next() ) {
4028           sm = aMesh->MeshElements( vExp.Current() );
4029           if ( sm ) {
4030             nSeamIt = sm->GetNodes();
4031             while ( nSeamIt->more() )
4032               seamNodes.push_back( nSeamIt->next() );
4033           }
4034         }
4035         // loop on nodes on seam
4036         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4037         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4038           const SMDS_MeshNode* nSeam = *noSeIt;
4039           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4040           if ( n_uv == uvMap.end() )
4041             continue;
4042           // set the first UV
4043           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4044           // set the second UV
4045           listUV.push_back( *n_uv->second );
4046           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4047           if ( uvMap2.empty() )
4048             uvMap2 = uvMap; // copy the uvMap contents
4049           uvMap2[ nSeam ] = &listUV.back();
4050
4051           // collect movable nodes linked to ones on seam in nodesNearSeam
4052           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4053           while ( eIt->more() ) {
4054             const SMDS_MeshElement* e = eIt->next();
4055             int nbUseMap1 = 0, nbUseMap2 = 0;
4056             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4057             int nn = 0, nbn =  e->NbNodes();
4058             if(e->IsQuadratic()) nbn = nbn/2;
4059             while ( nn++ < nbn )
4060             {
4061               const SMDS_MeshNode* n =
4062                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4063               if (n == nSeam ||
4064                   setMovableNodes.find( n ) == setMovableNodes.end() )
4065                 continue;
4066               // add only nodes being closer to uv2 than to uv1
4067               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4068                            0.5 * ( n->Y() + nSeam->Y() ),
4069                            0.5 * ( n->Z() + nSeam->Z() ));
4070               gp_XY uv;
4071               getClosestUV( projector, pMid, uv );
4072               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4073                 nodesNearSeam.insert( n );
4074                 nbUseMap2++;
4075               }
4076               else
4077                 nbUseMap1++;
4078             }
4079             // for centroidalSmooth all element nodes must
4080             // be on one side of a seam
4081             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4082               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4083               nn = 0;
4084               while ( nn++ < nbn ) {
4085                 const SMDS_MeshNode* n =
4086                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4087                 setMovableNodes.erase( n );
4088               }
4089             }
4090           }
4091         } // loop on nodes on seam
4092       } // loop on edge of a face
4093     } // if ( !face.IsNull() )
4094
4095     if ( setMovableNodes.empty() ) {
4096       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4097       continue; // goto next face
4098     }
4099
4100     // -------------
4101     // SMOOTHING //
4102     // -------------
4103
4104     int it = -1;
4105     double maxRatio = -1., maxDisplacement = -1.;
4106     set<const SMDS_MeshNode*>::iterator nodeToMove;
4107     for ( it = 0; it < theNbIterations; it++ ) {
4108       maxDisplacement = 0.;
4109       nodeToMove = setMovableNodes.begin();
4110       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4111         const SMDS_MeshNode* node = (*nodeToMove);
4112         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4113
4114         // smooth
4115         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4116         if ( theSmoothMethod == LAPLACIAN )
4117           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4118         else
4119           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4120
4121         // node displacement
4122         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4123         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4124         if ( aDispl > maxDisplacement )
4125           maxDisplacement = aDispl;
4126       }
4127       // no node movement => exit
4128       //if ( maxDisplacement < 1.e-16 ) {
4129       if ( maxDisplacement < disttol ) {
4130         MESSAGE("-- no node movement --");
4131         break;
4132       }
4133
4134       // check elements quality
4135       maxRatio  = 0;
4136       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4137       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4138         const SMDS_MeshElement* elem = (*elemIt);
4139         if ( !elem || elem->GetType() != SMDSAbs_Face )
4140           continue;
4141         SMESH::Controls::TSequenceOfXYZ aPoints;
4142         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4143           double aValue = aQualityFunc.GetValue( aPoints );
4144           if ( aValue > maxRatio )
4145             maxRatio = aValue;
4146         }
4147       }
4148       if ( maxRatio <= theTgtAspectRatio ) {
4149         MESSAGE("-- quality achived --");
4150         break;
4151       }
4152       if (it+1 == theNbIterations) {
4153         MESSAGE("-- Iteration limit exceeded --");
4154       }
4155     } // smoothing iterations
4156
4157     MESSAGE(" Face id: " << *fId <<
4158             " Nb iterstions: " << it <<
4159             " Displacement: " << maxDisplacement <<
4160             " Aspect Ratio " << maxRatio);
4161
4162     // ---------------------------------------
4163     // new nodes positions are computed,
4164     // record movement in DS and set new UV
4165     // ---------------------------------------
4166     nodeToMove = setMovableNodes.begin();
4167     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4168       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4169       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4170       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4171       if ( node_uv != uvMap.end() ) {
4172         gp_XY* uv = node_uv->second;
4173         node->SetPosition
4174           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4175       }
4176     }
4177
4178     // move medium nodes of quadratic elements
4179     if ( isQuadratic )
4180     {
4181       SMESH_MesherHelper helper( *GetMesh() );
4182       helper.SetSubShape( face );
4183       vector<const SMDS_MeshNode*> nodes;
4184       bool checkUV;
4185       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4187       {
4188         const SMDS_MeshElement* QF = *elemIt;
4189         if ( QF->IsQuadratic() )
4190         {
4191           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4192                         SMDS_MeshElement::iterator() );
4193           nodes.push_back( nodes[0] );
4194           gp_Pnt xyz;
4195           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4196           {
4197             if ( !surface.IsNull() )
4198             {
4199               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4200               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4201               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4202               xyz = surface->Value( uv.X(), uv.Y() );
4203             }
4204             else {
4205               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4206             }
4207             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4208               // we have to move a medium node
4209               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4210           }
4211         }
4212       }
4213     }
4214
4215   } // loop on face ids
4216
4217 }
4218
4219 namespace
4220 {
4221   //=======================================================================
4222   //function : isReverse
4223   //purpose  : Return true if normal of prevNodes is not co-directied with
4224   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4225   //           iNotSame is where prevNodes and nextNodes are different.
4226   //           If result is true then future volume orientation is OK
4227   //=======================================================================
4228
4229   bool isReverse(const SMDS_MeshElement*             face,
4230                  const vector<const SMDS_MeshNode*>& prevNodes,
4231                  const vector<const SMDS_MeshNode*>& nextNodes,
4232                  const int                           iNotSame)
4233   {
4234
4235     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4236     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4237     gp_XYZ extrDir( pN - pP ), faceNorm;
4238     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4239
4240     return faceNorm * extrDir < 0.0;
4241   }
4242
4243   //================================================================================
4244   /*!
4245    * \brief Assure that theElemSets[0] holds elements, not nodes
4246    */
4247   //================================================================================
4248
4249   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4250   {
4251     if ( !theElemSets[0].empty() &&
4252          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4253     {
4254       std::swap( theElemSets[0], theElemSets[1] );
4255     }
4256     else if ( !theElemSets[1].empty() &&
4257               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4258     {
4259       std::swap( theElemSets[0], theElemSets[1] );
4260     }
4261   }
4262 }
4263
4264 //=======================================================================
4265 /*!
4266  * \brief Create elements by sweeping an element
4267  * \param elem - element to sweep
4268  * \param newNodesItVec - nodes generated from each node of the element
4269  * \param newElems - generated elements
4270  * \param nbSteps - number of sweeping steps
4271  * \param srcElements - to append elem for each generated element
4272  */
4273 //=======================================================================
4274
4275 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4276                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4277                                     list<const SMDS_MeshElement*>&        newElems,
4278                                     const int                             nbSteps,
4279                                     SMESH_SequenceOfElemPtr&              srcElements)
4280 {
4281   //MESSAGE("sweepElement " << nbSteps);
4282   SMESHDS_Mesh* aMesh = GetMeshDS();
4283
4284   const int           nbNodes = elem->NbNodes();
4285   const int         nbCorners = elem->NbCornerNodes();
4286   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4287                                                           polyhedron creation !!! */
4288   // Loop on elem nodes:
4289   // find new nodes and detect same nodes indices
4290   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4291   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4292   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4293   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4294
4295   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4296   vector<int> sames(nbNodes);
4297   vector<bool> isSingleNode(nbNodes);
4298
4299   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4300     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4301     const SMDS_MeshNode*                         node = nnIt->first;
4302     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4303     if ( listNewNodes.empty() )
4304       return;
4305
4306     itNN   [ iNode ] = listNewNodes.begin();
4307     prevNod[ iNode ] = node;
4308     nextNod[ iNode ] = listNewNodes.front();
4309
4310     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4311                                                              corner node of linear */
4312     if ( prevNod[ iNode ] != nextNod [ iNode ])
4313       nbDouble += !isSingleNode[iNode];
4314
4315     if( iNode < nbCorners ) { // check corners only
4316       if ( prevNod[ iNode ] == nextNod [ iNode ])
4317         sames[nbSame++] = iNode;
4318       else
4319         iNotSameNode = iNode;
4320     }
4321   }
4322
4323   if ( nbSame == nbNodes || nbSame > 2) {
4324     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4325     return;
4326   }
4327
4328   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4329   {
4330     // fix nodes order to have bottom normal external
4331     if ( baseType == SMDSEntity_Polygon )
4332     {
4333       std::reverse( itNN.begin(), itNN.end() );
4334       std::reverse( prevNod.begin(), prevNod.end() );
4335       std::reverse( midlNod.begin(), midlNod.end() );
4336       std::reverse( nextNod.begin(), nextNod.end() );
4337       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4338     }
4339     else
4340     {
4341       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4342       SMDS_MeshCell::applyInterlace( ind, itNN );
4343       SMDS_MeshCell::applyInterlace( ind, prevNod );
4344       SMDS_MeshCell::applyInterlace( ind, nextNod );
4345       SMDS_MeshCell::applyInterlace( ind, midlNod );
4346       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4347       if ( nbSame > 0 )
4348       {
4349         sames[nbSame] = iNotSameNode;
4350         for ( int j = 0; j <= nbSame; ++j )
4351           for ( size_t i = 0; i < ind.size(); ++i )
4352             if ( ind[i] == sames[j] )
4353             {
4354               sames[j] = i;
4355               break;
4356             }
4357         iNotSameNode = sames[nbSame];
4358       }
4359     }
4360   }
4361   else if ( elem->GetType() == SMDSAbs_Edge )
4362   {
4363     // orient a new face same as adjacent one
4364     int i1, i2;
4365     const SMDS_MeshElement* e;
4366     TIDSortedElemSet dummy;
4367     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4368         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4369         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4370     {
4371       // there is an adjacent face, check order of nodes in it
4372       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4373       if ( sameOrder )
4374       {
4375         std::swap( itNN[0],    itNN[1] );
4376         std::swap( prevNod[0], prevNod[1] );
4377         std::swap( nextNod[0], nextNod[1] );
4378         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4379         if ( nbSame > 0 )
4380           sames[0] = 1 - sames[0];
4381         iNotSameNode = 1 - iNotSameNode;
4382       }
4383     }
4384   }
4385
4386   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4387   if ( nbSame > 0 ) {
4388     iSameNode    = sames[ nbSame-1 ];
4389     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4390     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4391     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4392   }
4393
4394   // make new elements
4395   for (int iStep = 0; iStep < nbSteps; iStep++ )
4396   {
4397     // get next nodes
4398     for ( iNode = 0; iNode < nbNodes; iNode++ )
4399     {
4400       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4401       nextNod[ iNode ] = *itNN[ iNode ]++;
4402     }
4403
4404     SMDS_MeshElement* aNewElem = 0;
4405     /*if(!elem->IsPoly())*/ {
4406       switch ( baseType ) {
4407       case SMDSEntity_0D:
4408       case SMDSEntity_Node: { // sweep NODE
4409         if ( nbSame == 0 ) {
4410           if ( isSingleNode[0] )
4411             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4412           else
4413             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4414         }
4415         else
4416           return;
4417         break;
4418       }
4419       case SMDSEntity_Edge: { // sweep EDGE
4420         if ( nbDouble == 0 )
4421         {
4422           if ( nbSame == 0 ) // ---> quadrangle
4423             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4424                                       nextNod[ 1 ], nextNod[ 0 ] );
4425           else               // ---> triangle
4426             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4427                                       nextNod[ iNotSameNode ] );
4428         }
4429         else                 // ---> polygon
4430         {
4431           vector<const SMDS_MeshNode*> poly_nodes;
4432           poly_nodes.push_back( prevNod[0] );
4433           poly_nodes.push_back( prevNod[1] );
4434           if ( prevNod[1] != nextNod[1] )
4435           {
4436             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4437             poly_nodes.push_back( nextNod[1] );
4438           }
4439           if ( prevNod[0] != nextNod[0] )
4440           {
4441             poly_nodes.push_back( nextNod[0] );
4442             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4443           }
4444           switch ( poly_nodes.size() ) {
4445           case 3:
4446             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4447             break;
4448           case 4:
4449             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4450                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4451             break;
4452           default:
4453             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4454           }
4455         }
4456         break;
4457       }
4458       case SMDSEntity_Triangle: // TRIANGLE --->
4459         {
4460           if ( nbDouble > 0 ) break;
4461           if ( nbSame == 0 )       // ---> pentahedron
4462             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4463                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4464
4465           else if ( nbSame == 1 )  // ---> pyramid
4466             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4467                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4468                                          nextNod[ iSameNode ]);
4469
4470           else // 2 same nodes:       ---> tetrahedron
4471             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4472                                          nextNod[ iNotSameNode ]);
4473           break;
4474         }
4475       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4476         {
4477           if ( nbSame == 2 )
4478             return;
4479           if ( nbDouble+nbSame == 2 )
4480           {
4481             if(nbSame==0) {      // ---> quadratic quadrangle
4482               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4483                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4484             }
4485             else { //(nbSame==1) // ---> quadratic triangle
4486               if(sames[0]==2) {
4487                 return; // medium node on axis
4488               }
4489               else if(sames[0]==0)
4490                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4491                                           prevNod[2], midlNod[1], nextNod[2] );
4492               else // sames[0]==1
4493                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4494                                           prevNod[2], nextNod[2], midlNod[0]);
4495             }
4496           }
4497           else if ( nbDouble == 3 )
4498           {
4499             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4500               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4501                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4502             }
4503           }
4504           else
4505             return;
4506           break;
4507         }
4508       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4509         if ( nbDouble > 0 ) break;
4510
4511         if ( nbSame == 0 )       // ---> hexahedron
4512           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4513                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4514
4515         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4516           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4517                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4518                                        nextNod[ iSameNode ]);
4519           newElems.push_back( aNewElem );
4520           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4521                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4522                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4523         }
4524         else if ( nbSame == 2 ) { // ---> pentahedron
4525           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4526             // iBeforeSame is same too
4527             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4528                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4529                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4530           else
4531             // iAfterSame is same too
4532             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4533                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4534                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4535         }
4536         break;
4537       }
4538       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4539       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4540         if ( nbDouble+nbSame != 3 ) break;
4541         if(nbSame==0) {
4542           // --->  pentahedron with 15 nodes
4543           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4544                                        nextNod[0], nextNod[1], nextNod[2],
4545                                        prevNod[3], prevNod[4], prevNod[5],
4546                                        nextNod[3], nextNod[4], nextNod[5],
4547                                        midlNod[0], midlNod[1], midlNod[2]);
4548         }
4549         else if(nbSame==1) {
4550           // --->  2d order pyramid of 13 nodes
4551           int apex = iSameNode;
4552           int i0 = ( apex + 1 ) % nbCorners;
4553           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4554           int i0a = apex + 3;
4555           int i1a = i1 + 3;
4556           int i01 = i0 + 3;
4557           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4558                                       nextNod[i0], nextNod[i1], prevNod[apex],
4559                                       prevNod[i01], midlNod[i0],
4560                                       nextNod[i01], midlNod[i1],
4561                                       prevNod[i1a], prevNod[i0a],
4562                                       nextNod[i0a], nextNod[i1a]);
4563         }
4564         else if(nbSame==2) {
4565           // --->  2d order tetrahedron of 10 nodes
4566           int n1 = iNotSameNode;
4567           int n2 = ( n1 + 1             ) % nbCorners;
4568           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4569           int n12 = n1 + 3;
4570           int n23 = n2 + 3;
4571           int n31 = n3 + 3;
4572           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4573                                        prevNod[n12], prevNod[n23], prevNod[n31],
4574                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4575         }
4576         break;
4577       }
4578       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4579         if( nbSame == 0 ) {
4580           if ( nbDouble != 4 ) break;
4581           // --->  hexahedron with 20 nodes
4582           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4583                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4584                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4585                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4586                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4587         }
4588         else if(nbSame==1) {
4589           // ---> pyramid + pentahedron - can not be created since it is needed
4590           // additional middle node at the center of face
4591           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4592           return;
4593         }
4594         else if( nbSame == 2 ) {
4595           if ( nbDouble != 2 ) break;
4596           // --->  2d order Pentahedron with 15 nodes
4597           int n1,n2,n4,n5;
4598           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4599             // iBeforeSame is same too
4600             n1 = iBeforeSame;
4601             n2 = iOpposSame;
4602             n4 = iSameNode;
4603             n5 = iAfterSame;
4604           }
4605           else {
4606             // iAfterSame is same too
4607             n1 = iSameNode;
4608             n2 = iBeforeSame;
4609             n4 = iAfterSame;
4610             n5 = iOpposSame;
4611           }
4612           int n12 = n2 + 4;
4613           int n45 = n4 + 4;
4614           int n14 = n1 + 4;
4615           int n25 = n5 + 4;
4616           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4617                                        prevNod[n4], prevNod[n5], nextNod[n5],
4618                                        prevNod[n12], midlNod[n2], nextNod[n12],
4619                                        prevNod[n45], midlNod[n5], nextNod[n45],
4620                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4621         }
4622         break;
4623       }
4624       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4625
4626         if( nbSame == 0 && nbDouble == 9 ) {
4627           // --->  tri-quadratic hexahedron with 27 nodes
4628           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4629                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4630                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4631                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4632                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4633                                        prevNod[8], // bottom center
4634                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4635                                        nextNod[8], // top center
4636                                        midlNod[8]);// elem center
4637         }
4638         else
4639         {
4640           return;
4641         }
4642         break;
4643       }
4644       case SMDSEntity_Polygon: { // sweep POLYGON
4645
4646         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4647           // --->  hexagonal prism
4648           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4649                                        prevNod[3], prevNod[4], prevNod[5],
4650                                        nextNod[0], nextNod[1], nextNod[2],
4651                                        nextNod[3], nextNod[4], nextNod[5]);
4652         }
4653         break;
4654       }
4655       case SMDSEntity_Ball:
4656         return;
4657
4658       default:
4659         break;
4660       } // switch ( baseType )
4661     } // scope
4662
4663     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4664     {
4665       if ( baseType != SMDSEntity_Polygon )
4666       {
4667         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4668         SMDS_MeshCell::applyInterlace( ind, prevNod );
4669         SMDS_MeshCell::applyInterlace( ind, nextNod );
4670         SMDS_MeshCell::applyInterlace( ind, midlNod );
4671         SMDS_MeshCell::applyInterlace( ind, itNN );
4672         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4673         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4674       }
4675       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4676       vector<int> quantities (nbNodes + 2);
4677       polyedre_nodes.clear();
4678       quantities.clear();
4679
4680       // bottom of prism
4681       for (int inode = 0; inode < nbNodes; inode++)
4682         polyedre_nodes.push_back( prevNod[inode] );
4683       quantities.push_back( nbNodes );
4684
4685       // top of prism
4686       polyedre_nodes.push_back( nextNod[0] );
4687       for (int inode = nbNodes; inode-1; --inode )
4688         polyedre_nodes.push_back( nextNod[inode-1] );
4689       quantities.push_back( nbNodes );
4690
4691       // side faces
4692       for (int iface = 0; iface < nbNodes; iface++)
4693       {
4694         const int prevNbNodes = polyedre_nodes.size();
4695         int inextface = (iface+1) % nbNodes;
4696         polyedre_nodes.push_back( prevNod[inextface] );
4697         polyedre_nodes.push_back( prevNod[iface] );
4698         if ( prevNod[iface] != nextNod[iface] )
4699         {
4700           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4701           polyedre_nodes.push_back( nextNod[iface] );
4702         }
4703         if ( prevNod[inextface] != nextNod[inextface] )
4704         {
4705           polyedre_nodes.push_back( nextNod[inextface] );
4706           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4707         }
4708         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4709         if ( nbFaceNodes > 2 )
4710           quantities.push_back( nbFaceNodes );
4711         else // degenerated face
4712           polyedre_nodes.resize( prevNbNodes );
4713       }
4714       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4715
4716     } //  // try to create a polyherdal prism
4717
4718     if ( aNewElem ) {
4719       newElems.push_back( aNewElem );
4720       myLastCreatedElems.Append(aNewElem);
4721       srcElements.Append( elem );
4722     }
4723
4724     // set new prev nodes
4725     for ( iNode = 0; iNode < nbNodes; iNode++ )
4726       prevNod[ iNode ] = nextNod[ iNode ];
4727
4728   } // loop on steps
4729 }
4730
4731 //=======================================================================
4732 /*!
4733  * \brief Create 1D and 2D elements around swept elements
4734  * \param mapNewNodes - source nodes and ones generated from them
4735  * \param newElemsMap - source elements and ones generated from them
4736  * \param elemNewNodesMap - nodes generated from each node of each element
4737  * \param elemSet - all swept elements
4738  * \param nbSteps - number of sweeping steps
4739  * \param srcElements - to append elem for each generated element
4740  */
4741 //=======================================================================
4742
4743 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4744                                   TTElemOfElemListMap &    newElemsMap,
4745                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4746                                   TIDSortedElemSet&        elemSet,
4747                                   const int                nbSteps,
4748                                   SMESH_SequenceOfElemPtr& srcElements)
4749 {
4750   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4751   SMESHDS_Mesh* aMesh = GetMeshDS();
4752
4753   // Find nodes belonging to only one initial element - sweep them into edges.
4754
4755   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4756   for ( ; nList != mapNewNodes.end(); nList++ )
4757   {
4758     const SMDS_MeshNode* node =
4759       static_cast<const SMDS_MeshNode*>( nList->first );
4760     if ( newElemsMap.count( node ))
4761       continue; // node was extruded into edge
4762     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4763     int nbInitElems = 0;
4764     const SMDS_MeshElement* el = 0;
4765     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4766     while ( eIt->more() && nbInitElems < 2 ) {
4767       el = eIt->next();
4768       SMDSAbs_ElementType type = el->GetType();
4769       if ( type == SMDSAbs_Volume || type < highType ) continue;
4770       if ( type > highType ) {
4771         nbInitElems = 0;
4772         highType = type;
4773       }
4774       nbInitElems += elemSet.count(el);
4775     }
4776     if ( nbInitElems < 2 ) {
4777       bool NotCreateEdge = el && el->IsMediumNode(node);
4778       if(!NotCreateEdge) {
4779         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4780         list<const SMDS_MeshElement*> newEdges;
4781         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4782       }
4783     }
4784   }
4785
4786   // Make a ceiling for each element ie an equal element of last new nodes.
4787   // Find free links of faces - make edges and sweep them into faces.
4788
4789   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4790   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4791   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4792   {
4793     const SMDS_MeshElement* elem = itElem->first;
4794     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4795
4796     if(itElem->second.size()==0) continue;
4797
4798     const bool isQuadratic = elem->IsQuadratic();
4799
4800     if ( elem->GetType() == SMDSAbs_Edge ) {
4801       // create a ceiling edge
4802       if ( !isQuadratic ) {
4803         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4804                                vecNewNodes[ 1 ]->second.back())) {
4805           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4806                                                    vecNewNodes[ 1 ]->second.back()));
4807           srcElements.Append( elem );
4808         }
4809       }
4810       else {
4811         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4812                                vecNewNodes[ 1 ]->second.back(),
4813                                vecNewNodes[ 2 ]->second.back())) {
4814           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4815                                                    vecNewNodes[ 1 ]->second.back(),
4816                                                    vecNewNodes[ 2 ]->second.back()));
4817           srcElements.Append( elem );
4818         }
4819       }
4820     }
4821     if ( elem->GetType() != SMDSAbs_Face )
4822       continue;
4823
4824     bool hasFreeLinks = false;
4825
4826     TIDSortedElemSet avoidSet;
4827     avoidSet.insert( elem );
4828
4829     set<const SMDS_MeshNode*> aFaceLastNodes;
4830     int iNode, nbNodes = vecNewNodes.size();
4831     if ( !isQuadratic ) {
4832       // loop on the face nodes
4833       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4834         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4835         // look for free links of the face
4836         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4837         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4838         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4839         // check if a link n1-n2 is free
4840         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4841           hasFreeLinks = true;
4842           // make a new edge and a ceiling for a new edge
4843           const SMDS_MeshElement* edge;
4844           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4845             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4846             srcElements.Append( myLastCreatedElems.Last() );
4847           }
4848           n1 = vecNewNodes[ iNode ]->second.back();
4849           n2 = vecNewNodes[ iNext ]->second.back();
4850           if ( !aMesh->FindEdge( n1, n2 )) {
4851             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4852             srcElements.Append( edge );
4853           }
4854         }
4855       }
4856     }
4857     else { // elem is quadratic face
4858       int nbn = nbNodes/2;
4859       for ( iNode = 0; iNode < nbn; iNode++ ) {
4860         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4861         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4862         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4863         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4864         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4865         // check if a link is free
4866         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4867              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4868              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4869           hasFreeLinks = true;
4870           // make an edge and a ceiling for a new edge
4871           // find medium node
4872           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4873             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4874             srcElements.Append( elem );
4875           }
4876           n1 = vecNewNodes[ iNode ]->second.back();
4877           n2 = vecNewNodes[ iNext ]->second.back();
4878           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4879           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4880             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4881             srcElements.Append( elem );
4882           }
4883         }
4884       }
4885       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4886         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4887       }
4888     }
4889
4890     // sweep free links into faces
4891
4892     if ( hasFreeLinks )  {
4893       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4894       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4895
4896       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4897       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4898       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4899         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4900         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4901       }
4902       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4903         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4904         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4905       }
4906       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4907         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4908         std::advance( v, volNb );
4909         // find indices of free faces of a volume and their source edges
4910         list< int > freeInd;
4911         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4912         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4913         int iF, nbF = vTool.NbFaces();
4914         for ( iF = 0; iF < nbF; iF ++ ) {
4915           if (vTool.IsFreeFace( iF ) &&
4916               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4917               initNodeSet != faceNodeSet) // except an initial face
4918           {
4919             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4920               continue;
4921             if ( faceNodeSet == initNodeSetNoCenter )
4922               continue;
4923             freeInd.push_back( iF );
4924             // find source edge of a free face iF
4925             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4926             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4927             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4928                                    initNodeSet.begin(), initNodeSet.end(),
4929                                    commonNodes.begin());
4930             if ( (*v)->IsQuadratic() )
4931               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4932             else
4933               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4934 #ifdef _DEBUG_
4935             if ( !srcEdges.back() )
4936             {
4937               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4938                    << iF << " of volume #" << vTool.ID() << endl;
4939             }
4940 #endif
4941           }
4942         }
4943         if ( freeInd.empty() )
4944           continue;
4945
4946         // create faces for all steps;
4947         // if such a face has been already created by sweep of edge,
4948         // assure that its orientation is OK
4949         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4950           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4951           vTool.SetExternalNormal();
4952           const int nextShift = vTool.IsForward() ? +1 : -1;
4953           list< int >::iterator ind = freeInd.begin();
4954           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4955           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4956           {
4957             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4958             int nbn = vTool.NbFaceNodes( *ind );
4959             const SMDS_MeshElement * f = 0;
4960             if ( nbn == 3 )              ///// triangle
4961             {
4962               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4963               if ( !f ||
4964                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4965               {
4966                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4967                                                      nodes[ 1 ],
4968                                                      nodes[ 1 + nextShift ] };
4969                 if ( f )
4970                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4971                 else
4972                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4973                                                             newOrder[ 2 ] ));
4974               }
4975             }
4976             else if ( nbn == 4 )       ///// quadrangle
4977             {
4978               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4979               if ( !f ||
4980                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4981               {
4982                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4983                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4984                 if ( f )
4985                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4986                 else
4987                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4988                                                             newOrder[ 2 ], newOrder[ 3 ]));
4989               }
4990             }
4991             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4992             {
4993               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4994               if ( !f ||
4995                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4996               {
4997                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4998                                                      nodes[2],
4999                                                      nodes[2 + 2*nextShift],
5000                                                      nodes[3 - 2*nextShift],
5001                                                      nodes[3],
5002                                                      nodes[3 + 2*nextShift]};
5003                 if ( f )
5004                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5005                 else
5006                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5007                                                             newOrder[ 1 ],
5008                                                             newOrder[ 2 ],
5009                                                             newOrder[ 3 ],
5010                                                             newOrder[ 4 ],
5011                                                             newOrder[ 5 ] ));
5012               }
5013             }
5014             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5015             {
5016               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5017                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5018               if ( !f ||
5019                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5020               {
5021                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5022                                                      nodes[4 - 2*nextShift],
5023                                                      nodes[4],
5024                                                      nodes[4 + 2*nextShift],
5025                                                      nodes[1],
5026                                                      nodes[5 - 2*nextShift],
5027                                                      nodes[5],
5028                                                      nodes[5 + 2*nextShift] };
5029                 if ( f )
5030                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5031                 else
5032                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5033                                                            newOrder[ 2 ], newOrder[ 3 ],
5034                                                            newOrder[ 4 ], newOrder[ 5 ],
5035                                                            newOrder[ 6 ], newOrder[ 7 ]));
5036               }
5037             }
5038             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5039             {
5040               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5041                                       SMDSAbs_Face, /*noMedium=*/false);
5042               if ( !f ||
5043                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5044               {
5045                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5046                                                      nodes[4 - 2*nextShift],
5047                                                      nodes[4],
5048                                                      nodes[4 + 2*nextShift],
5049                                                      nodes[1],
5050                                                      nodes[5 - 2*nextShift],
5051                                                      nodes[5],
5052                                                      nodes[5 + 2*nextShift],
5053                                                      nodes[8] };
5054                 if ( f )
5055                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5056                 else
5057                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5058                                                            newOrder[ 2 ], newOrder[ 3 ],
5059                                                            newOrder[ 4 ], newOrder[ 5 ],
5060                                                            newOrder[ 6 ], newOrder[ 7 ],
5061                                                            newOrder[ 8 ]));
5062               }
5063             }
5064             else  //////// polygon
5065             {
5066               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5067               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5068               if ( !f ||
5069                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5070               {
5071                 if ( !vTool.IsForward() )
5072                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5073                 if ( f )
5074                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5075                 else
5076                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5077               }
5078             }
5079
5080             while ( srcElements.Length() < myLastCreatedElems.Length() )
5081               srcElements.Append( *srcEdge );
5082
5083           }  // loop on free faces
5084
5085           // go to the next volume
5086           iVol = 0;
5087           while ( iVol++ < nbVolumesByStep ) v++;
5088
5089         } // loop on steps
5090       } // loop on volumes of one step
5091     } // sweep free links into faces
5092
5093     // Make a ceiling face with a normal external to a volume
5094
5095     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5096     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5097     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5098
5099     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5100       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5101       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5102     }
5103     if ( iF >= 0 ) {
5104       lastVol.SetExternalNormal();
5105       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5106       int nbn = lastVol.NbFaceNodes( iF );
5107       // we do not use this->AddElement() because nodes are interlaced
5108       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5109       if ( !hasFreeLinks ||
5110            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5111       {
5112         if ( nbn == 3 )
5113           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5114
5115         else if ( nbn == 4 )
5116           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5117
5118         else if ( nbn == 6 && isQuadratic )
5119           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5120                                                     nodes[1], nodes[3], nodes[5]));
5121         else if ( nbn == 7 && isQuadratic )
5122           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5123                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5124         else if ( nbn == 8 && isQuadratic )
5125           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5126                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5127         else if ( nbn == 9 && isQuadratic )
5128           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5129                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5130                                                     nodes[8]));
5131         else
5132           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5133
5134         while ( srcElements.Length() < myLastCreatedElems.Length() )
5135           srcElements.Append( elem );
5136       }
5137     }
5138   } // loop on swept elements
5139 }
5140
5141 //=======================================================================
5142 //function : RotationSweep
5143 //purpose  :
5144 //=======================================================================
5145
5146 SMESH_MeshEditor::PGroupIDs
5147 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5148                                 const gp_Ax1&      theAxis,
5149                                 const double       theAngle,
5150                                 const int          theNbSteps,
5151                                 const double       theTol,
5152                                 const bool         theMakeGroups,
5153                                 const bool         theMakeWalls)
5154 {
5155   myLastCreatedElems.Clear();
5156   myLastCreatedNodes.Clear();
5157
5158   // source elements for each generated one
5159   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5160
5161   MESSAGE( "RotationSweep()");
5162   gp_Trsf aTrsf;
5163   aTrsf.SetRotation( theAxis, theAngle );
5164   gp_Trsf aTrsf2;
5165   aTrsf2.SetRotation( theAxis, theAngle/2. );
5166
5167   gp_Lin aLine( theAxis );
5168   double aSqTol = theTol * theTol;
5169
5170   SMESHDS_Mesh* aMesh = GetMeshDS();
5171
5172   TNodeOfNodeListMap mapNewNodes;
5173   TElemOfVecOfNnlmiMap mapElemNewNodes;
5174   TTElemOfElemListMap newElemsMap;
5175
5176   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5177                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5178                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5179   // loop on theElemSets
5180   setElemsFirst( theElemSets );
5181   TIDSortedElemSet::iterator itElem;
5182   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5183   {
5184     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5185     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5186       const SMDS_MeshElement* elem = *itElem;
5187       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5188         continue;
5189       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5190       newNodesItVec.reserve( elem->NbNodes() );
5191
5192       // loop on elem nodes
5193       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5194       while ( itN->more() )
5195       {
5196         // check if a node has been already sweeped
5197         const SMDS_MeshNode* node = cast2Node( itN->next() );
5198
5199         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5200         double coord[3];
5201         aXYZ.Coord( coord[0], coord[1], coord[2] );
5202         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5203
5204         TNodeOfNodeListMapItr nIt =
5205           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5206         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5207         if ( listNewNodes.empty() )
5208         {
5209           // check if we are to create medium nodes between corner ones
5210           bool needMediumNodes = false;
5211           if ( isQuadraticMesh )
5212           {
5213             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5214             while (it->more() && !needMediumNodes )
5215             {
5216               const SMDS_MeshElement* invElem = it->next();
5217               if ( invElem != elem && !theElems.count( invElem )) continue;
5218               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5219               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5220                 needMediumNodes = true;
5221             }
5222           }
5223
5224           // make new nodes
5225           const SMDS_MeshNode * newNode = node;
5226           for ( int i = 0; i < theNbSteps; i++ ) {
5227             if ( !isOnAxis ) {
5228               if ( needMediumNodes )  // create a medium node
5229               {
5230                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5231                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5232                 myLastCreatedNodes.Append(newNode);
5233                 srcNodes.Append( node );
5234                 listNewNodes.push_back( newNode );
5235                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5236               }
5237               else {
5238                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5239               }
5240               // create a corner node
5241               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5242               myLastCreatedNodes.Append(newNode);
5243               srcNodes.Append( node );
5244               listNewNodes.push_back( newNode );
5245             }
5246             else {
5247               listNewNodes.push_back( newNode );
5248               // if ( needMediumNodes )
5249               //   listNewNodes.push_back( newNode );
5250             }
5251           }
5252         }
5253         newNodesItVec.push_back( nIt );
5254       }
5255       // make new elements
5256       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5257     }
5258   }
5259
5260   if ( theMakeWalls )
5261     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5262
5263   PGroupIDs newGroupIDs;
5264   if ( theMakeGroups )
5265     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5266
5267   return newGroupIDs;
5268 }
5269
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose  : standard construction
5273 //=======================================================================
5274
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5276                                             const int      theNbSteps,
5277                                             const int      theFlags,
5278                                             const double   theTolerance):
5279   myDir( theStep ),
5280   myFlags( theFlags ),
5281   myTolerance( theTolerance ),
5282   myElemsToUse( NULL )
5283 {
5284   mySteps = new TColStd_HSequenceOfReal;
5285   const double stepSize = theStep.Magnitude();
5286   for (int i=1; i<=theNbSteps; i++ )
5287     mySteps->Append( stepSize );
5288
5289   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5290       ( theTolerance > 0 ))
5291   {
5292     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5293   }
5294   else
5295   {
5296     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5297   }
5298 }
5299
5300 //=======================================================================
5301 //function : ExtrusParam
5302 //purpose  : steps are given explicitly
5303 //=======================================================================
5304
5305 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5306                                             Handle(TColStd_HSequenceOfReal) theSteps,
5307                                             const int                       theFlags,
5308                                             const double                    theTolerance):
5309   myDir( theDir ),
5310   mySteps( theSteps ),
5311   myFlags( theFlags ),
5312   myTolerance( theTolerance ),
5313   myElemsToUse( NULL )
5314 {
5315   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5316       ( theTolerance > 0 ))
5317   {
5318     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5319   }
5320   else
5321   {
5322     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5323   }
5324 }
5325
5326 //=======================================================================
5327 //function : ExtrusParam
5328 //purpose  : for extrusion by normal
5329 //=======================================================================
5330
5331 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5332                                             const int    theNbSteps,
5333                                             const int    theFlags,
5334                                             const int    theDim ):
5335   myDir( 1,0,0 ),
5336   mySteps( new TColStd_HSequenceOfReal ),
5337   myFlags( theFlags ),
5338   myTolerance( 0 ),
5339   myElemsToUse( NULL )
5340 {
5341   for (int i = 0; i < theNbSteps; i++ )
5342     mySteps->Append( theStepSize );
5343
5344   if ( theDim == 1 )
5345   {
5346     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5347   }
5348   else
5349   {
5350     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5351   }
5352 }
5353
5354 //=======================================================================
5355 //function : ExtrusParam::SetElementsToUse
5356 //purpose  : stores elements to use for extrusion by normal, depending on
5357 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5358 //=======================================================================
5359
5360 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5361 {
5362   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5363 }
5364
5365 //=======================================================================
5366 //function : ExtrusParam::beginStepIter
5367 //purpose  : prepare iteration on steps
5368 //=======================================================================
5369
5370 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5371 {
5372   myWithMediumNodes = withMediumNodes;
5373   myNextStep = 1;
5374   myCurSteps.clear();
5375 }
5376 //=======================================================================
5377 //function : ExtrusParam::moreSteps
5378 //purpose  : are there more steps?
5379 //=======================================================================
5380
5381 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5382 {
5383   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5384 }
5385 //=======================================================================
5386 //function : ExtrusParam::nextStep
5387 //purpose  : returns the next step
5388 //=======================================================================
5389
5390 double SMESH_MeshEditor::ExtrusParam::nextStep()
5391 {
5392   double res = 0;
5393   if ( !myCurSteps.empty() )
5394   {
5395     res = myCurSteps.back();
5396     myCurSteps.pop_back();
5397   }
5398   else if ( myNextStep <= mySteps->Length() )
5399   {
5400     myCurSteps.push_back( mySteps->Value( myNextStep ));
5401     ++myNextStep;
5402     if ( myWithMediumNodes )
5403     {
5404       myCurSteps.back() /= 2.;
5405       myCurSteps.push_back( myCurSteps.back() );
5406     }
5407     res = nextStep();
5408   }
5409   return res;
5410 }
5411
5412 //=======================================================================
5413 //function : ExtrusParam::makeNodesByDir
5414 //purpose  : create nodes for standard extrusion
5415 //=======================================================================
5416
5417 int SMESH_MeshEditor::ExtrusParam::
5418 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5419                 const SMDS_MeshNode*              srcNode,
5420                 std::list<const SMDS_MeshNode*> & newNodes,
5421                 const bool                        makeMediumNodes)
5422 {
5423   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5424
5425   int nbNodes = 0;
5426   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5427   {
5428     p += myDir.XYZ() * nextStep();
5429     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5430     newNodes.push_back( newNode );
5431   }
5432   return nbNodes;
5433 }
5434
5435 //=======================================================================
5436 //function : ExtrusParam::makeNodesByDirAndSew
5437 //purpose  : create nodes for standard extrusion with sewing
5438 //=======================================================================
5439
5440 int SMESH_MeshEditor::ExtrusParam::
5441 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5442                       const SMDS_MeshNode*              srcNode,
5443                       std::list<const SMDS_MeshNode*> & newNodes,
5444                       const bool                        makeMediumNodes)
5445 {
5446   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5447
5448   int nbNodes = 0;
5449   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5450   {
5451     P1 += myDir.XYZ() * nextStep();
5452
5453     // try to search in sequence of existing nodes
5454     // if myNodes.Length()>0 we 'nave to use given sequence
5455     // else - use all nodes of mesh
5456     const SMDS_MeshNode * node = 0;
5457     if ( myNodes.Length() > 0 ) {
5458       int i;
5459       for(i=1; i<=myNodes.Length(); i++) {
5460         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5461         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5462         {
5463           node = myNodes.Value(i);
5464           break;
5465         }
5466       }
5467     }
5468     else {
5469       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5470       while(itn->more()) {
5471         SMESH_TNodeXYZ P2( itn->next() );
5472         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5473         {
5474           node = P2._node;
5475           break;
5476         }
5477       }
5478     }
5479
5480     if ( !node )
5481       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5482
5483     newNodes.push_back( node );
5484
5485   } // loop on steps
5486
5487   return nbNodes;
5488 }
5489
5490 //=======================================================================
5491 //function : ExtrusParam::makeNodesByNormal2D
5492 //purpose  : create nodes for extrusion using normals of faces
5493 //=======================================================================
5494
5495 int SMESH_MeshEditor::ExtrusParam::
5496 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5497                      const SMDS_MeshNode*              srcNode,
5498                      std::list<const SMDS_MeshNode*> & newNodes,
5499                      const bool                        makeMediumNodes)
5500 {
5501   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5502
5503   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5504
5505   // get normals to faces sharing srcNode
5506   vector< gp_XYZ > norms, baryCenters;
5507   gp_XYZ norm, avgNorm( 0,0,0 );
5508   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5509   while ( faceIt->more() )
5510   {
5511     const SMDS_MeshElement* face = faceIt->next();
5512     if ( myElemsToUse && !myElemsToUse->count( face ))
5513       continue;
5514     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5515     {
5516       norms.push_back( norm );
5517       avgNorm += norm;
5518       if ( !alongAvgNorm )
5519       {
5520         gp_XYZ bc(0,0,0);
5521         int nbN = 0;
5522         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5523           bc += SMESH_TNodeXYZ( nIt->next() );
5524         baryCenters.push_back( bc / nbN );
5525       }
5526     }
5527   }
5528
5529   if ( norms.empty() ) return 0;
5530
5531   double normSize = avgNorm.Modulus();
5532   if ( normSize < std::numeric_limits<double>::min() )
5533     return 0;
5534
5535   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5536   {
5537     myDir = avgNorm;
5538     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5539   }
5540
5541   avgNorm /= normSize;
5542
5543   int nbNodes = 0;
5544   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5545   {
5546     gp_XYZ pNew = p;
5547     double stepSize = nextStep();
5548
5549     if ( norms.size() > 1 )
5550     {
5551       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5552       {
5553         // translate plane of a face
5554         baryCenters[ iF ] += norms[ iF ] * stepSize;
5555
5556         // find point of intersection of the face plane located at baryCenters[ iF ]
5557         // and avgNorm located at pNew
5558         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5559         double dot  = ( norms[ iF ] * avgNorm );
5560         if ( dot < std::numeric_limits<double>::min() )
5561           dot = stepSize * 1e-3;
5562         double step = -( norms[ iF ] * pNew + d ) / dot;
5563         pNew += step * avgNorm;
5564       }
5565     }
5566     else
5567     {
5568       pNew += stepSize * avgNorm;
5569     }
5570     p = pNew;
5571
5572     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5573     newNodes.push_back( newNode );
5574   }
5575   return nbNodes;
5576 }
5577
5578 //=======================================================================
5579 //function : ExtrusParam::makeNodesByNormal1D
5580 //purpose  : create nodes for extrusion using normals of edges
5581 //=======================================================================
5582
5583 int SMESH_MeshEditor::ExtrusParam::
5584 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5585                      const SMDS_MeshNode*              srcNode,
5586                      std::list<const SMDS_MeshNode*> & newNodes,
5587                      const bool                        makeMediumNodes)
5588 {
5589   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5590   return 0;
5591 }
5592
5593 //=======================================================================
5594 //function : ExtrusionSweep
5595 //purpose  :
5596 //=======================================================================
5597
5598 SMESH_MeshEditor::PGroupIDs
5599 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5600                                   const gp_Vec&        theStep,
5601                                   const int            theNbSteps,
5602                                   TTElemOfElemListMap& newElemsMap,
5603                                   const int            theFlags,
5604                                   const double         theTolerance)
5605 {
5606   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5607   return ExtrusionSweep( theElems, aParams, newElemsMap );
5608 }
5609
5610
5611 //=======================================================================
5612 //function : ExtrusionSweep
5613 //purpose  :
5614 //=======================================================================
5615
5616 SMESH_MeshEditor::PGroupIDs
5617 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5618                                   ExtrusParam&         theParams,
5619                                   TTElemOfElemListMap& newElemsMap)
5620 {
5621   myLastCreatedElems.Clear();
5622   myLastCreatedNodes.Clear();
5623
5624   // source elements for each generated one
5625   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5626
5627   SMESHDS_Mesh* aMesh = GetMeshDS();
5628
5629   setElemsFirst( theElemSets );
5630   const int nbSteps = theParams.NbSteps();
5631   theParams.SetElementsToUse( theElemSets[0] );
5632
5633   TNodeOfNodeListMap mapNewNodes;
5634   //TNodeOfNodeVecMap mapNewNodes;
5635   TElemOfVecOfNnlmiMap mapElemNewNodes;
5636   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5637
5638   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5639                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5640                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5641   // loop on theElems
5642   TIDSortedElemSet::iterator itElem;
5643   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5644   {
5645     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5646     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5647     {
5648       // check element type
5649       const SMDS_MeshElement* elem = *itElem;
5650       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5651         continue;
5652
5653       const size_t nbNodes = elem->NbNodes();
5654       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5655       newNodesItVec.reserve( nbNodes );
5656
5657       // loop on elem nodes
5658       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5659       while ( itN->more() )
5660       {
5661         // check if a node has been already sweeped
5662         const SMDS_MeshNode* node = cast2Node( itN->next() );
5663         TNodeOfNodeListMap::iterator nIt =
5664           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5665         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5666         if ( listNewNodes.empty() )
5667         {
5668           // make new nodes
5669
5670           // check if we are to create medium nodes between corner ones
5671           bool needMediumNodes = false;
5672           if ( isQuadraticMesh )
5673           {
5674             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5675             while (it->more() && !needMediumNodes )
5676             {
5677               const SMDS_MeshElement* invElem = it->next();
5678               if ( invElem != elem && !theElems.count( invElem )) continue;
5679               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5680               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5681                 needMediumNodes = true;
5682             }
5683           }
5684           // create nodes for all steps
5685           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5686           {
5687             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5688             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5689             {
5690               myLastCreatedNodes.Append( *newNodesIt );
5691               srcNodes.Append( node );
5692             }
5693           }
5694           else
5695           {
5696             break; // newNodesItVec will be shorter than nbNodes
5697           }
5698         }
5699         newNodesItVec.push_back( nIt );
5700       }
5701       // make new elements
5702       if ( newNodesItVec.size() == nbNodes )
5703         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5704     }
5705   }
5706
5707   if ( theParams.ToMakeBoundary() ) {
5708     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5709   }
5710   PGroupIDs newGroupIDs;
5711   if ( theParams.ToMakeGroups() )
5712     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5713
5714   return newGroupIDs;
5715 }
5716
5717 //=======================================================================
5718 //function : ExtrusionAlongTrack
5719 //purpose  :
5720 //=======================================================================
5721 SMESH_MeshEditor::Extrusion_Error
5722 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5723                                        SMESH_subMesh*       theTrack,
5724                                        const SMDS_MeshNode* theN1,
5725                                        const bool           theHasAngles,
5726                                        list<double>&        theAngles,
5727                                        const bool           theLinearVariation,
5728                                        const bool           theHasRefPoint,
5729                                        const gp_Pnt&        theRefPoint,
5730                                        const bool           theMakeGroups)
5731 {
5732   MESSAGE("ExtrusionAlongTrack");
5733   myLastCreatedElems.Clear();
5734   myLastCreatedNodes.Clear();
5735
5736   int aNbE;
5737   std::list<double> aPrms;
5738   TIDSortedElemSet::iterator itElem;
5739
5740   gp_XYZ aGC;
5741   TopoDS_Edge aTrackEdge;
5742   TopoDS_Vertex aV1, aV2;
5743
5744   SMDS_ElemIteratorPtr aItE;
5745   SMDS_NodeIteratorPtr aItN;
5746   SMDSAbs_ElementType aTypeE;
5747
5748   TNodeOfNodeListMap mapNewNodes;
5749
5750   // 1. Check data
5751   aNbE = theElements[0].size() + theElements[1].size();
5752   // nothing to do
5753   if ( !aNbE )
5754     return EXTR_NO_ELEMENTS;
5755
5756   // 1.1 Track Pattern
5757   ASSERT( theTrack );
5758
5759   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5760
5761   aItE = pSubMeshDS->GetElements();
5762   while ( aItE->more() ) {
5763     const SMDS_MeshElement* pE = aItE->next();
5764     aTypeE = pE->GetType();
5765     // Pattern must contain links only
5766     if ( aTypeE != SMDSAbs_Edge )
5767       return EXTR_PATH_NOT_EDGE;
5768   }
5769
5770   list<SMESH_MeshEditor_PathPoint> fullList;
5771
5772   const TopoDS_Shape& aS = theTrack->GetSubShape();
5773   // Sub-shape for the Pattern must be an Edge or Wire
5774   if( aS.ShapeType() == TopAbs_EDGE ) {
5775     aTrackEdge = TopoDS::Edge( aS );
5776     // the Edge must not be degenerated
5777     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5778       return EXTR_BAD_PATH_SHAPE;
5779     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5780     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5781     const SMDS_MeshNode* aN1 = aItN->next();
5782     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5783     const SMDS_MeshNode* aN2 = aItN->next();
5784     // starting node must be aN1 or aN2
5785     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5786       return EXTR_BAD_STARTING_NODE;
5787     aItN = pSubMeshDS->GetNodes();
5788     while ( aItN->more() ) {
5789       const SMDS_MeshNode* pNode = aItN->next();
5790       const SMDS_EdgePosition* pEPos =
5791         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5792       double aT = pEPos->GetUParameter();
5793       aPrms.push_back( aT );
5794     }
5795     //Extrusion_Error err =
5796     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5797   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5798     list< SMESH_subMesh* > LSM;
5799     TopTools_SequenceOfShape Edges;
5800     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5801     while(itSM->more()) {
5802       SMESH_subMesh* SM = itSM->next();
5803       LSM.push_back(SM);
5804       const TopoDS_Shape& aS = SM->GetSubShape();
5805       Edges.Append(aS);
5806     }
5807     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5808     int startNid = theN1->GetID();
5809     TColStd_MapOfInteger UsedNums;
5810
5811     int NbEdges = Edges.Length();
5812     int i = 1;
5813     for(; i<=NbEdges; i++) {
5814       int k = 0;
5815       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5816       for(; itLSM!=LSM.end(); itLSM++) {
5817         k++;
5818         if(UsedNums.Contains(k)) continue;
5819         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5820         SMESH_subMesh* locTrack = *itLSM;
5821         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5822         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5823         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5824         const SMDS_MeshNode* aN1 = aItN->next();
5825         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5826         const SMDS_MeshNode* aN2 = aItN->next();
5827         // starting node must be aN1 or aN2
5828         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5829         // 2. Collect parameters on the track edge
5830         aPrms.clear();
5831         aItN = locMeshDS->GetNodes();
5832         while ( aItN->more() ) {
5833           const SMDS_MeshNode* pNode = aItN->next();
5834           const SMDS_EdgePosition* pEPos =
5835             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5836           double aT = pEPos->GetUParameter();
5837           aPrms.push_back( aT );
5838         }
5839         list<SMESH_MeshEditor_PathPoint> LPP;
5840         //Extrusion_Error err =
5841         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5842         LLPPs.push_back(LPP);
5843         UsedNums.Add(k);
5844         // update startN for search following egde
5845         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5846         else startNid = aN1->GetID();
5847         break;
5848       }
5849     }
5850     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5851     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5852     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5853     for(; itPP!=firstList.end(); itPP++) {
5854       fullList.push_back( *itPP );
5855     }
5856     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5857     fullList.pop_back();
5858     itLLPP++;
5859     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5860       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5861       itPP = currList.begin();
5862       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5863       gp_Dir D1 = PP1.Tangent();
5864       gp_Dir D2 = PP2.Tangent();
5865       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5866                            (D1.Z()+D2.Z())/2 ) );
5867       PP1.SetTangent(Dnew);
5868       fullList.push_back(PP1);
5869       itPP++;
5870       for(; itPP!=firstList.end(); itPP++) {
5871         fullList.push_back( *itPP );
5872       }
5873       PP1 = fullList.back();
5874       fullList.pop_back();
5875     }
5876     // if wire not closed
5877     fullList.push_back(PP1);
5878     // else ???
5879   }
5880   else {
5881     return EXTR_BAD_PATH_SHAPE;
5882   }
5883
5884   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5885                           theHasRefPoint, theRefPoint, theMakeGroups);
5886 }
5887
5888
5889 //=======================================================================
5890 //function : ExtrusionAlongTrack
5891 //purpose  :
5892 //=======================================================================
5893 SMESH_MeshEditor::Extrusion_Error
5894 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5895                                        SMESH_Mesh*          theTrack,
5896                                        const SMDS_MeshNode* theN1,
5897                                        const bool           theHasAngles,
5898                                        list<double>&        theAngles,
5899                                        const bool           theLinearVariation,
5900                                        const bool           theHasRefPoint,
5901                                        const gp_Pnt&        theRefPoint,
5902                                        const bool           theMakeGroups)
5903 {
5904   myLastCreatedElems.Clear();
5905   myLastCreatedNodes.Clear();
5906
5907   int aNbE;
5908   std::list<double> aPrms;
5909   TIDSortedElemSet::iterator itElem;
5910
5911   gp_XYZ aGC;
5912   TopoDS_Edge aTrackEdge;
5913   TopoDS_Vertex aV1, aV2;
5914
5915   SMDS_ElemIteratorPtr aItE;
5916   SMDS_NodeIteratorPtr aItN;
5917   SMDSAbs_ElementType aTypeE;
5918
5919   TNodeOfNodeListMap mapNewNodes;
5920
5921   // 1. Check data
5922   aNbE = theElements[0].size() + theElements[1].size();
5923   // nothing to do
5924   if ( !aNbE )
5925     return EXTR_NO_ELEMENTS;
5926
5927   // 1.1 Track Pattern
5928   ASSERT( theTrack );
5929
5930   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5931
5932   aItE = pMeshDS->elementsIterator();
5933   while ( aItE->more() ) {
5934     const SMDS_MeshElement* pE = aItE->next();
5935     aTypeE = pE->GetType();
5936     // Pattern must contain links only
5937     if ( aTypeE != SMDSAbs_Edge )
5938       return EXTR_PATH_NOT_EDGE;
5939   }
5940
5941   list<SMESH_MeshEditor_PathPoint> fullList;
5942
5943   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5944
5945   if ( !theTrack->HasShapeToMesh() ) {
5946     //Mesh without shape
5947     const SMDS_MeshNode* currentNode = NULL;
5948     const SMDS_MeshNode* prevNode = theN1;
5949     std::vector<const SMDS_MeshNode*> aNodesList;
5950     aNodesList.push_back(theN1);
5951     int nbEdges = 0, conn=0;
5952     const SMDS_MeshElement* prevElem = NULL;
5953     const SMDS_MeshElement* currentElem = NULL;
5954     int totalNbEdges = theTrack->NbEdges();
5955     SMDS_ElemIteratorPtr nIt;
5956
5957     //check start node
5958     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5959       return EXTR_BAD_STARTING_NODE;
5960     }
5961
5962     conn = nbEdgeConnectivity(theN1);
5963     if( conn != 1 )
5964       return EXTR_PATH_NOT_EDGE;
5965
5966     aItE = theN1->GetInverseElementIterator();
5967     prevElem = aItE->next();
5968     currentElem = prevElem;
5969     //Get all nodes
5970     if(totalNbEdges == 1 ) {
5971       nIt = currentElem->nodesIterator();
5972       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5973       if(currentNode == prevNode)
5974         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5975       aNodesList.push_back(currentNode);
5976     } else {
5977       nIt = currentElem->nodesIterator();
5978       while( nIt->more() ) {
5979         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980         if(currentNode == prevNode)
5981           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5982         aNodesList.push_back(currentNode);
5983
5984         //case of the closed mesh
5985         if(currentNode == theN1) {
5986           nbEdges++;
5987           break;
5988         }
5989
5990         conn = nbEdgeConnectivity(currentNode);
5991         if(conn > 2) {
5992           return EXTR_PATH_NOT_EDGE;
5993         }else if( conn == 1 && nbEdges > 0 ) {
5994           //End of the path
5995           nbEdges++;
5996           break;
5997         }else {
5998           prevNode = currentNode;
5999           aItE = currentNode->GetInverseElementIterator();
6000           currentElem = aItE->next();
6001           if( currentElem  == prevElem)
6002             currentElem = aItE->next();
6003           nIt = currentElem->nodesIterator();
6004           prevElem = currentElem;
6005           nbEdges++;
6006         }
6007       }
6008     }
6009
6010     if(nbEdges != totalNbEdges)
6011       return EXTR_PATH_NOT_EDGE;
6012
6013     TopTools_SequenceOfShape Edges;
6014     double x1,x2,y1,y2,z1,z2;
6015     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6016     int startNid = theN1->GetID();
6017     for(int i = 1; i < aNodesList.size(); i++) {
6018       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6019       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6020       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6021       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6022       list<SMESH_MeshEditor_PathPoint> LPP;
6023       aPrms.clear();
6024       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6025       LLPPs.push_back(LPP);
6026       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6027       else startNid = aNodesList[i-1]->GetID();
6028
6029     }
6030
6031     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6032     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6033     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6034     for(; itPP!=firstList.end(); itPP++) {
6035       fullList.push_back( *itPP );
6036     }
6037
6038     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6039     SMESH_MeshEditor_PathPoint PP2;
6040     fullList.pop_back();
6041     itLLPP++;
6042     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6043       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6044       itPP = currList.begin();
6045       PP2 = currList.front();
6046       gp_Dir D1 = PP1.Tangent();
6047       gp_Dir D2 = PP2.Tangent();
6048       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6049                            (D1.Z()+D2.Z())/2 ) );
6050       PP1.SetTangent(Dnew);
6051       fullList.push_back(PP1);
6052       itPP++;
6053       for(; itPP!=currList.end(); itPP++) {
6054         fullList.push_back( *itPP );
6055       }
6056       PP1 = fullList.back();
6057       fullList.pop_back();
6058     }
6059     fullList.push_back(PP1);
6060
6061   } // Sub-shape for the Pattern must be an Edge or Wire
6062   else if( aS.ShapeType() == TopAbs_EDGE ) {
6063     aTrackEdge = TopoDS::Edge( aS );
6064     // the Edge must not be degenerated
6065     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6066       return EXTR_BAD_PATH_SHAPE;
6067     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6068     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6069     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6070     // starting node must be aN1 or aN2
6071     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6072       return EXTR_BAD_STARTING_NODE;
6073     aItN = pMeshDS->nodesIterator();
6074     while ( aItN->more() ) {
6075       const SMDS_MeshNode* pNode = aItN->next();
6076       if( pNode==aN1 || pNode==aN2 ) continue;
6077       const SMDS_EdgePosition* pEPos =
6078         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6079       double aT = pEPos->GetUParameter();
6080       aPrms.push_back( aT );
6081     }
6082     //Extrusion_Error err =
6083     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6084   }
6085   else if( aS.ShapeType() == TopAbs_WIRE ) {
6086     list< SMESH_subMesh* > LSM;
6087     TopTools_SequenceOfShape Edges;
6088     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6089     for(; eExp.More(); eExp.Next()) {
6090       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6091       if( SMESH_Algo::isDegenerated(E) ) continue;
6092       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6093       if(SM) {
6094         LSM.push_back(SM);
6095         Edges.Append(E);
6096       }
6097     }
6098     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6099     TopoDS_Vertex aVprev;
6100     TColStd_MapOfInteger UsedNums;
6101     int NbEdges = Edges.Length();
6102     int i = 1;
6103     for(; i<=NbEdges; i++) {
6104       int k = 0;
6105       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6106       for(; itLSM!=LSM.end(); itLSM++) {
6107         k++;
6108         if(UsedNums.Contains(k)) continue;
6109         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6110         SMESH_subMesh* locTrack = *itLSM;
6111         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6112         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6113         bool aN1isOK = false, aN2isOK = false;
6114         if ( aVprev.IsNull() ) {
6115           // if previous vertex is not yet defined, it means that we in the beginning of wire
6116           // and we have to find initial vertex corresponding to starting node theN1
6117           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6118           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6119           // starting node must be aN1 or aN2
6120           aN1isOK = ( aN1 && aN1 == theN1 );
6121           aN2isOK = ( aN2 && aN2 == theN1 );
6122         }
6123         else {
6124           // we have specified ending vertex of the previous edge on the previous iteration
6125           // and we have just to check that it corresponds to any vertex in current segment
6126           aN1isOK = aVprev.IsSame( aV1 );
6127           aN2isOK = aVprev.IsSame( aV2 );
6128         }
6129         if ( !aN1isOK && !aN2isOK ) continue;
6130         // 2. Collect parameters on the track edge
6131         aPrms.clear();
6132         aItN = locMeshDS->GetNodes();
6133         while ( aItN->more() ) {
6134           const SMDS_MeshNode*     pNode = aItN->next();
6135           const SMDS_EdgePosition* pEPos =
6136             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6137           double aT = pEPos->GetUParameter();
6138           aPrms.push_back( aT );
6139         }
6140         list<SMESH_MeshEditor_PathPoint> LPP;
6141         //Extrusion_Error err =
6142         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6143         LLPPs.push_back(LPP);
6144         UsedNums.Add(k);
6145         // update startN for search following egde
6146         if ( aN1isOK ) aVprev = aV2;
6147         else           aVprev = aV1;
6148         break;
6149       }
6150     }
6151     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6152     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6153     fullList.splice( fullList.end(), firstList );
6154
6155     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6156     fullList.pop_back();
6157     itLLPP++;
6158     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6159       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6160       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6161       gp_Dir D1 = PP1.Tangent();
6162       gp_Dir D2 = PP2.Tangent();
6163       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6164       PP1.SetTangent(Dnew);
6165       fullList.push_back(PP1);
6166       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6167       PP1 = fullList.back();
6168       fullList.pop_back();
6169     }
6170     // if wire not closed
6171     fullList.push_back(PP1);
6172     // else ???
6173   }
6174   else {
6175     return EXTR_BAD_PATH_SHAPE;
6176   }
6177
6178   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6179                           theHasRefPoint, theRefPoint, theMakeGroups);
6180 }
6181
6182
6183 //=======================================================================
6184 //function : MakeEdgePathPoints
6185 //purpose  : auxilary for ExtrusionAlongTrack
6186 //=======================================================================
6187 SMESH_MeshEditor::Extrusion_Error
6188 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6189                                      const TopoDS_Edge&                aTrackEdge,
6190                                      bool                              FirstIsStart,
6191                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6192 {
6193   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6194   aTolVec=1.e-7;
6195   aTolVec2=aTolVec*aTolVec;
6196   double aT1, aT2;
6197   TopoDS_Vertex aV1, aV2;
6198   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6199   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6200   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6201   // 2. Collect parameters on the track edge
6202   aPrms.push_front( aT1 );
6203   aPrms.push_back( aT2 );
6204   // sort parameters
6205   aPrms.sort();
6206   if( FirstIsStart ) {
6207     if ( aT1 > aT2 ) {
6208       aPrms.reverse();
6209     }
6210   }
6211   else {
6212     if ( aT2 > aT1 ) {
6213       aPrms.reverse();
6214     }
6215   }
6216   // 3. Path Points
6217   SMESH_MeshEditor_PathPoint aPP;
6218   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6219   std::list<double>::iterator aItD = aPrms.begin();
6220   for(; aItD != aPrms.end(); ++aItD) {
6221     double aT = *aItD;
6222     gp_Pnt aP3D;
6223     gp_Vec aVec;
6224     aC3D->D1( aT, aP3D, aVec );
6225     aL2 = aVec.SquareMagnitude();
6226     if ( aL2 < aTolVec2 )
6227       return EXTR_CANT_GET_TANGENT;
6228     gp_Dir aTgt( aVec );
6229     aPP.SetPnt( aP3D );
6230     aPP.SetTangent( aTgt );
6231     aPP.SetParameter( aT );
6232     LPP.push_back(aPP);
6233   }
6234   return EXTR_OK;
6235 }
6236
6237
6238 //=======================================================================
6239 //function : MakeExtrElements
6240 //purpose  : auxilary for ExtrusionAlongTrack
6241 //=======================================================================
6242 SMESH_MeshEditor::Extrusion_Error
6243 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6244                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6245                                    const bool                        theHasAngles,
6246                                    list<double>&                     theAngles,
6247                                    const bool                        theLinearVariation,
6248                                    const bool                        theHasRefPoint,
6249                                    const gp_Pnt&                     theRefPoint,
6250                                    const bool                        theMakeGroups)
6251 {
6252   const int aNbTP = fullList.size();
6253   // Angles
6254   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6255     LinearAngleVariation(aNbTP-1, theAngles);
6256   // fill vector of path points with angles
6257   vector<SMESH_MeshEditor_PathPoint> aPPs;
6258   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6259   list<double>::iterator                 itAngles = theAngles.begin();
6260   aPPs.push_back( *itPP++ );
6261   for( ; itPP != fullList.end(); itPP++) {
6262     aPPs.push_back( *itPP );
6263     if ( theHasAngles && itAngles != theAngles.end() )
6264       aPPs.back().SetAngle( *itAngles++ );
6265   }
6266
6267   TNodeOfNodeListMap   mapNewNodes;
6268   TElemOfVecOfNnlmiMap mapElemNewNodes;
6269   TTElemOfElemListMap  newElemsMap;
6270   TIDSortedElemSet::iterator itElem;
6271   // source elements for each generated one
6272   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6273
6274   // 3. Center of rotation aV0
6275   gp_Pnt aV0 = theRefPoint;
6276   if ( !theHasRefPoint )
6277   {
6278     gp_XYZ aGC( 0.,0.,0. );
6279     TIDSortedElemSet newNodes;
6280
6281     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6282     {
6283       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6284       itElem = theElements.begin();
6285       for ( ; itElem != theElements.end(); itElem++ ) {
6286         const SMDS_MeshElement* elem = *itElem;
6287
6288         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289         while ( itN->more() ) {
6290           const SMDS_MeshElement* node = itN->next();
6291           if ( newNodes.insert( node ).second )
6292             aGC += SMESH_TNodeXYZ( node );
6293         }
6294       }
6295     }
6296     aGC /= newNodes.size();
6297     aV0.SetXYZ( aGC );
6298   } // if (!theHasRefPoint) {
6299
6300   // 4. Processing the elements
6301   SMESHDS_Mesh* aMesh = GetMeshDS();
6302
6303   setElemsFirst( theElemSets );
6304   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6305   {
6306     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6307     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6308       // check element type
6309       const SMDS_MeshElement* elem = *itElem;
6310       if ( !elem )
6311         continue;
6312       // SMDSAbs_ElementType aTypeE = elem->GetType();
6313       // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6314       //   continue;
6315
6316       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317       newNodesItVec.reserve( elem->NbNodes() );
6318
6319       // loop on elem nodes
6320       int nodeIndex = -1;
6321       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322       while ( itN->more() )
6323       {
6324         ++nodeIndex;
6325         // check if a node has been already processed
6326         const SMDS_MeshNode* node =
6327           static_cast<const SMDS_MeshNode*>( itN->next() );
6328         TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6329         if ( nIt == mapNewNodes.end() ) {
6330           nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6331           list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6332
6333           // make new nodes
6334           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6335           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6336           gp_Ax1 anAx1, anAxT1T0;
6337           gp_Dir aDT1x, aDT0x, aDT1T0;
6338
6339           aTolAng=1.e-4;
6340
6341           aV0x = aV0;
6342           aPN0 = SMESH_TNodeXYZ( node );
6343
6344           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6345           aP0x = aPP0.Pnt();
6346           aDT0x= aPP0.Tangent();
6347           //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6348
6349           for ( int j = 1; j < aNbTP; ++j ) {
6350             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6351             aP1x     = aPP1.Pnt();
6352             aDT1x    = aPP1.Tangent();
6353             aAngle1x = aPP1.Angle();
6354
6355             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6356             // Translation
6357             gp_Vec aV01x( aP0x, aP1x );
6358             aTrsf.SetTranslation( aV01x );
6359
6360             // traslated point
6361             aV1x = aV0x.Transformed( aTrsf );
6362             aPN1 = aPN0.Transformed( aTrsf );
6363
6364             // rotation 1 [ T1,T0 ]
6365             aAngleT1T0=-aDT1x.Angle( aDT0x );
6366             if (fabs(aAngleT1T0) > aTolAng) {
6367               aDT1T0=aDT1x^aDT0x;
6368               anAxT1T0.SetLocation( aV1x );
6369               anAxT1T0.SetDirection( aDT1T0 );
6370               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6371
6372               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6373             }
6374
6375             // rotation 2
6376             if ( theHasAngles ) {
6377               anAx1.SetLocation( aV1x );
6378               anAx1.SetDirection( aDT1x );
6379               aTrsfRot.SetRotation( anAx1, aAngle1x );
6380
6381               aPN1 = aPN1.Transformed( aTrsfRot );
6382             }
6383
6384             // make new node
6385             //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6386             if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6387               // create additional node
6388               double x = ( aPN1.X() + aPN0.X() )/2.;
6389               double y = ( aPN1.Y() + aPN0.Y() )/2.;
6390               double z = ( aPN1.Z() + aPN0.Z() )/2.;
6391               const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6392               myLastCreatedNodes.Append(newNode);
6393               srcNodes.Append( node );
6394               listNewNodes.push_back( newNode );
6395             }
6396             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6397             myLastCreatedNodes.Append(newNode);
6398             srcNodes.Append( node );
6399             listNewNodes.push_back( newNode );
6400
6401             aPN0 = aPN1;
6402             aP0x = aP1x;
6403             aV0x = aV1x;
6404             aDT0x = aDT1x;
6405           }
6406         }
6407
6408         else {
6409           // if current elem is quadratic and current node is not medium
6410           // we have to check - may be it is needed to insert additional nodes
6411           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6412             list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6413             if(listNewNodes.size()==aNbTP-1) {
6414               vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6415               gp_XYZ P(node->X(), node->Y(), node->Z());
6416               list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6417               int i;
6418               for(i=0; i<aNbTP-1; i++) {
6419                 const SMDS_MeshNode* N = *it;
6420                 double x = ( N->X() + P.X() )/2.;
6421                 double y = ( N->Y() + P.Y() )/2.;
6422                 double z = ( N->Z() + P.Z() )/2.;
6423                 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6424                 srcNodes.Append( node );
6425                 myLastCreatedNodes.Append(newN);
6426                 aNodes[2*i] = newN;
6427                 aNodes[2*i+1] = N;
6428                 P = gp_XYZ(N->X(),N->Y(),N->Z());
6429               }
6430               listNewNodes.clear();
6431               for(i=0; i<2*(aNbTP-1); i++) {
6432                 listNewNodes.push_back(aNodes[i]);
6433               }
6434             }
6435           }
6436         }
6437
6438         newNodesItVec.push_back( nIt );
6439       }
6440       // make new elements
6441       //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6442       //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6443       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6444     }
6445   }
6446
6447   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6448
6449   if ( theMakeGroups )
6450     generateGroups( srcNodes, srcElems, "extruded");
6451
6452   return EXTR_OK;
6453 }
6454
6455
6456 //=======================================================================
6457 //function : LinearAngleVariation
6458 //purpose  : auxilary for ExtrusionAlongTrack
6459 //=======================================================================
6460 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6461                                             list<double>& Angles)
6462 {
6463   int nbAngles = Angles.size();
6464   if( nbSteps > nbAngles ) {
6465     vector<double> theAngles(nbAngles);
6466     list<double>::iterator it = Angles.begin();
6467     int i = -1;
6468     for(; it!=Angles.end(); it++) {
6469       i++;
6470       theAngles[i] = (*it);
6471     }
6472     list<double> res;
6473     double rAn2St = double( nbAngles ) / double( nbSteps );
6474     double angPrev = 0, angle;
6475     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6476       double angCur = rAn2St * ( iSt+1 );
6477       double angCurFloor  = floor( angCur );
6478       double angPrevFloor = floor( angPrev );
6479       if ( angPrevFloor == angCurFloor )
6480         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6481       else {
6482         int iP = int( angPrevFloor );
6483         double angPrevCeil = ceil(angPrev);
6484         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6485
6486         int iC = int( angCurFloor );
6487         if ( iC < nbAngles )
6488           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6489
6490         iP = int( angPrevCeil );
6491         while ( iC-- > iP )
6492           angle += theAngles[ iC ];
6493       }
6494       res.push_back(angle);
6495       angPrev = angCur;
6496     }
6497     Angles.clear();
6498     it = res.begin();
6499     for(; it!=res.end(); it++)
6500       Angles.push_back( *it );
6501   }
6502 }
6503
6504
6505 //================================================================================
6506 /*!
6507  * \brief Move or copy theElements applying theTrsf to their nodes
6508  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6509  *  \param theTrsf - transformation to apply
6510  *  \param theCopy - if true, create translated copies of theElems
6511  *  \param theMakeGroups - if true and theCopy, create translated groups
6512  *  \param theTargetMesh - mesh to copy translated elements into
6513  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6514  */
6515 //================================================================================
6516
6517 SMESH_MeshEditor::PGroupIDs
6518 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6519                              const gp_Trsf&     theTrsf,
6520                              const bool         theCopy,
6521                              const bool         theMakeGroups,
6522                              SMESH_Mesh*        theTargetMesh)
6523 {
6524   myLastCreatedElems.Clear();
6525   myLastCreatedNodes.Clear();
6526
6527   bool needReverse = false;
6528   string groupPostfix;
6529   switch ( theTrsf.Form() ) {
6530   case gp_PntMirror:
6531     MESSAGE("gp_PntMirror");
6532     needReverse = true;
6533     groupPostfix = "mirrored";
6534     break;
6535   case gp_Ax1Mirror:
6536     MESSAGE("gp_Ax1Mirror");
6537     groupPostfix = "mirrored";
6538     break;
6539   case gp_Ax2Mirror:
6540     MESSAGE("gp_Ax2Mirror");
6541     needReverse = true;
6542     groupPostfix = "mirrored";
6543     break;
6544   case gp_Rotation:
6545     MESSAGE("gp_Rotation");
6546     groupPostfix = "rotated";
6547     break;
6548   case gp_Translation:
6549     MESSAGE("gp_Translation");
6550     groupPostfix = "translated";
6551     break;
6552   case gp_Scale:
6553     MESSAGE("gp_Scale");
6554     groupPostfix = "scaled";
6555     break;
6556   case gp_CompoundTrsf: // different scale by axis
6557     MESSAGE("gp_CompoundTrsf");
6558     groupPostfix = "scaled";
6559     break;
6560   default:
6561     MESSAGE("default");
6562     needReverse = false;
6563     groupPostfix = "transformed";
6564   }
6565
6566   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6567   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6568   SMESHDS_Mesh* aMesh    = GetMeshDS();
6569
6570
6571   // map old node to new one
6572   TNodeNodeMap nodeMap;
6573
6574   // elements sharing moved nodes; those of them which have all
6575   // nodes mirrored but are not in theElems are to be reversed
6576   TIDSortedElemSet inverseElemSet;
6577
6578   // source elements for each generated one
6579   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6580
6581   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6582   TIDSortedElemSet orphanNode;
6583
6584   if ( theElems.empty() ) // transform the whole mesh
6585   {
6586     // add all elements
6587     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6588     while ( eIt->more() ) theElems.insert( eIt->next() );
6589     // add orphan nodes
6590     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6591     while ( nIt->more() )
6592     {
6593       const SMDS_MeshNode* node = nIt->next();
6594       if ( node->NbInverseElements() == 0)
6595         orphanNode.insert( node );
6596     }
6597   }
6598
6599   // loop on elements to transform nodes : first orphan nodes then elems
6600   TIDSortedElemSet::iterator itElem;
6601   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6602   for (int i=0; i<2; i++)
6603   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6604     const SMDS_MeshElement* elem = *itElem;
6605     if ( !elem )
6606       continue;
6607
6608     // loop on elem nodes
6609     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6610     while ( itN->more() ) {
6611
6612       const SMDS_MeshNode* node = cast2Node( itN->next() );
6613       // check if a node has been already transformed
6614       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6615         nodeMap.insert( make_pair ( node, node ));
6616       if ( !n2n_isnew.second )
6617         continue;
6618
6619       double coord[3];
6620       coord[0] = node->X();
6621       coord[1] = node->Y();
6622       coord[2] = node->Z();
6623       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6624       if ( theTargetMesh ) {
6625         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6626         n2n_isnew.first->second = newNode;
6627         myLastCreatedNodes.Append(newNode);
6628         srcNodes.Append( node );
6629       }
6630       else if ( theCopy ) {
6631         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6632         n2n_isnew.first->second = newNode;
6633         myLastCreatedNodes.Append(newNode);
6634         srcNodes.Append( node );
6635       }
6636       else {
6637         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6638         // node position on shape becomes invalid
6639         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6640           ( SMDS_SpacePosition::originSpacePosition() );
6641       }
6642
6643       // keep inverse elements
6644       if ( !theCopy && !theTargetMesh && needReverse ) {
6645         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6646         while ( invElemIt->more() ) {
6647           const SMDS_MeshElement* iel = invElemIt->next();
6648           inverseElemSet.insert( iel );
6649         }
6650       }
6651     }
6652   }
6653
6654   // either create new elements or reverse mirrored ones
6655   if ( !theCopy && !needReverse && !theTargetMesh )
6656     return PGroupIDs();
6657
6658   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6659   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6660     theElems.insert( *invElemIt );
6661
6662   // Replicate or reverse elements
6663
6664   std::vector<int> iForw;
6665   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6666   {
6667     const SMDS_MeshElement* elem = *itElem;
6668     if ( !elem ) continue;
6669
6670     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6671     int                  nbNodes  = elem->NbNodes();
6672     if ( geomType == SMDSGeom_NONE ) continue; // node
6673
6674     switch ( geomType ) {
6675
6676     case SMDSGeom_POLYGON:  // ---------------------- polygon
6677       {
6678         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6679         int iNode = 0;
6680         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6681         while (itN->more()) {
6682           const SMDS_MeshNode* node =
6683             static_cast<const SMDS_MeshNode*>(itN->next());
6684           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6685           if (nodeMapIt == nodeMap.end())
6686             break; // not all nodes transformed
6687           if (needReverse) {
6688             // reverse mirrored faces and volumes
6689             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6690           } else {
6691             poly_nodes[iNode] = (*nodeMapIt).second;
6692           }
6693           iNode++;
6694         }
6695         if ( iNode != nbNodes )
6696           continue; // not all nodes transformed
6697
6698         if ( theTargetMesh ) {
6699           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6700           srcElems.Append( elem );
6701         }
6702         else if ( theCopy ) {
6703           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6704           srcElems.Append( elem );
6705         }
6706         else {
6707           aMesh->ChangePolygonNodes(elem, poly_nodes);
6708         }
6709       }
6710       break;
6711
6712     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6713       {
6714         const SMDS_VtkVolume* aPolyedre =
6715           dynamic_cast<const SMDS_VtkVolume*>( elem );
6716         if (!aPolyedre) {
6717           MESSAGE("Warning: bad volumic element");
6718           continue;
6719         }
6720
6721         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6722         vector<int> quantities; quantities.reserve( nbNodes );
6723
6724         bool allTransformed = true;
6725         int nbFaces = aPolyedre->NbFaces();
6726         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6727           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6728           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6729             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6730             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6731             if (nodeMapIt == nodeMap.end()) {
6732               allTransformed = false; // not all nodes transformed
6733             } else {
6734               poly_nodes.push_back((*nodeMapIt).second);
6735             }
6736             if ( needReverse && allTransformed )
6737               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6738           }
6739           quantities.push_back(nbFaceNodes);
6740         }
6741         if ( !allTransformed )
6742           continue; // not all nodes transformed
6743
6744         if ( theTargetMesh ) {
6745           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6746           srcElems.Append( elem );
6747         }
6748         else if ( theCopy ) {
6749           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6750           srcElems.Append( elem );
6751         }
6752         else {
6753           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6754         }
6755       }
6756       break;
6757
6758     case SMDSGeom_BALL: // -------------------- Ball
6759       {
6760         if ( !theCopy && !theTargetMesh ) continue;
6761
6762         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6763         if (nodeMapIt == nodeMap.end())
6764           continue; // not all nodes transformed
6765
6766         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6767         if ( theTargetMesh ) {
6768           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6769           srcElems.Append( elem );
6770         }
6771         else {
6772           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6773           srcElems.Append( elem );
6774         }
6775       }
6776       break;
6777
6778     default: // ----------------------- Regular elements
6779
6780       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6781       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6782       const std::vector<int>& i = needReverse ? iRev : iForw;
6783
6784       // find transformed nodes
6785       vector<const SMDS_MeshNode*> nodes(nbNodes);
6786       int iNode = 0;
6787       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6788       while ( itN->more() ) {
6789         const SMDS_MeshNode* node =
6790           static_cast<const SMDS_MeshNode*>( itN->next() );
6791         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6792         if ( nodeMapIt == nodeMap.end() )
6793           break; // not all nodes transformed
6794         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6795       }
6796       if ( iNode != nbNodes )
6797         continue; // not all nodes transformed
6798
6799       if ( theTargetMesh ) {
6800         if ( SMDS_MeshElement* copy =
6801              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6802           myLastCreatedElems.Append( copy );
6803           srcElems.Append( elem );
6804         }
6805       }
6806       else if ( theCopy ) {
6807         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6808           srcElems.Append( elem );
6809       }
6810       else {
6811         // reverse element as it was reversed by transformation
6812         if ( nbNodes > 2 )
6813           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6814       }
6815     } // switch ( geomType )
6816
6817   } // loop on elements
6818
6819   PGroupIDs newGroupIDs;
6820
6821   if ( ( theMakeGroups && theCopy ) ||
6822        ( theMakeGroups && theTargetMesh ) )
6823     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6824
6825   return newGroupIDs;
6826 }
6827
6828 //=======================================================================
6829 /*!
6830  * \brief Create groups of elements made during transformation
6831  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6832  *  \param elemGens - elements making corresponding myLastCreatedElems
6833  *  \param postfix - to append to names of new groups
6834  *  \param targetMesh - mesh to create groups in
6835  *  \param topPresent - is there "top" elements that are created by sweeping
6836  */
6837 //=======================================================================
6838
6839 SMESH_MeshEditor::PGroupIDs
6840 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6841                                  const SMESH_SequenceOfElemPtr& elemGens,
6842                                  const std::string&             postfix,
6843                                  SMESH_Mesh*                    targetMesh,
6844                                  const bool                     topPresent)
6845 {
6846   PGroupIDs newGroupIDs( new list<int> );
6847   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6848
6849   // Sort existing groups by types and collect their names
6850
6851   // containers to store an old group and generated new ones;
6852   // 1st new group is for result elems of different type than a source one;
6853   // 2nd new group is for same type result elems ("top" group at extrusion)
6854   using boost::tuple;
6855   using boost::make_tuple;
6856   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6857   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6858   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6859   // group names
6860   set< string > groupNames;
6861
6862   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6863   if ( !groupIt->more() ) return newGroupIDs;
6864
6865   int newGroupID = mesh->GetGroupIds().back()+1;
6866   while ( groupIt->more() )
6867   {
6868     SMESH_Group * group = groupIt->next();
6869     if ( !group ) continue;
6870     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6871     if ( !groupDS || groupDS->IsEmpty() ) continue;
6872     groupNames.insert    ( group->GetName() );
6873     groupDS->SetStoreName( group->GetName() );
6874     const SMDSAbs_ElementType type = groupDS->GetType();
6875     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6876     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6877     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6878     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6879   }
6880
6881   // Loop on nodes and elements to add them in new groups
6882
6883   vector< const SMDS_MeshElement* > resultElems;
6884   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6885   {
6886     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6887     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6888     if ( gens.Length() != elems.Length() )
6889       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6890
6891     // loop on created elements
6892     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6893     {
6894       const SMDS_MeshElement* sourceElem = gens( iElem );
6895       if ( !sourceElem ) {
6896         MESSAGE("generateGroups(): NULL source element");
6897         continue;
6898       }
6899       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6900       if ( groupsOldNew.empty() ) { // no groups of this type at all
6901         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6902           ++iElem; // skip all elements made by sourceElem
6903         continue;
6904       }
6905       // collect all elements made by the iElem-th sourceElem
6906       resultElems.clear();
6907       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6908         if ( resElem != sourceElem )
6909           resultElems.push_back( resElem );
6910       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6911         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6912           if ( resElem != sourceElem )
6913             resultElems.push_back( resElem );
6914
6915       const SMDS_MeshElement* topElem = 0;
6916       if ( isNodes ) // there must be a top element
6917       {
6918         topElem = resultElems.back();
6919         resultElems.pop_back();
6920       }
6921       else
6922       {
6923         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6924         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6925           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6926           {
6927             topElem = *resElemIt;
6928             *resElemIt = 0; // erase *resElemIt
6929             break;
6930           }
6931       }
6932       // add resultElems to groups originted from ones the sourceElem belongs to
6933       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6934       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6935       {
6936         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6937         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6938         {
6939           // fill in a new group
6940           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6941           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6942           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6943             if ( *resElemIt )
6944               newGroup.Add( *resElemIt );
6945
6946           // fill a "top" group
6947           if ( topElem )
6948           {
6949             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6950             newTopGroup.Add( topElem );
6951          }
6952         }
6953       }
6954     } // loop on created elements
6955   }// loop on nodes and elements
6956
6957   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6958
6959   list<int> topGrouIds;
6960   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6961   {
6962     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6963     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6964                                       orderedOldNewGroups[i]->get<2>() };
6965     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6966     {
6967       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6968       if ( newGroupDS->IsEmpty() )
6969       {
6970         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6971       }
6972       else
6973       {
6974         // set group type
6975         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6976
6977         // make a name
6978         const bool isTop = ( topPresent &&
6979                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6980                              is2nd );
6981
6982         string name = oldGroupDS->GetStoreName();
6983         { // remove trailing whitespaces (issue 22599)
6984           size_t size = name.size();
6985           while ( size > 1 && isspace( name[ size-1 ]))
6986             --size;
6987           if ( size != name.size() )
6988           {
6989             name.resize( size );
6990             oldGroupDS->SetStoreName( name.c_str() );
6991           }
6992         }
6993         if ( !targetMesh ) {
6994           string suffix = ( isTop ? "top": postfix.c_str() );
6995           name += "_";
6996           name += suffix;
6997           int nb = 1;
6998           while ( !groupNames.insert( name ).second ) // name exists
6999             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7000         }
7001         else if ( isTop ) {
7002           name += "_top";
7003         }
7004         newGroupDS->SetStoreName( name.c_str() );
7005
7006         // make a SMESH_Groups
7007         mesh->AddGroup( newGroupDS );
7008         if ( isTop )
7009           topGrouIds.push_back( newGroupDS->GetID() );
7010         else
7011           newGroupIDs->push_back( newGroupDS->GetID() );
7012       }
7013     }
7014   }
7015   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7016
7017   return newGroupIDs;
7018 }
7019
7020 //================================================================================
7021 /*!
7022  * \brief Return list of group of nodes close to each other within theTolerance
7023  *        Search among theNodes or in the whole mesh if theNodes is empty using
7024  *        an Octree algorithm
7025  */
7026 //================================================================================
7027
7028 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7029                                             const double         theTolerance,
7030                                             TListOfListOfNodes & theGroupsOfNodes)
7031 {
7032   myLastCreatedElems.Clear();
7033   myLastCreatedNodes.Clear();
7034
7035   if ( theNodes.empty() )
7036   { // get all nodes in the mesh
7037     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7038     while ( nIt->more() )
7039       theNodes.insert( theNodes.end(),nIt->next());
7040   }
7041
7042   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7043 }
7044
7045 //=======================================================================
7046 //function : SimplifyFace
7047 //purpose  :
7048 //=======================================================================
7049
7050 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7051                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7052                                     vector<int>&                         quantities) const
7053 {
7054   int nbNodes = faceNodes.size();
7055
7056   if (nbNodes < 3)
7057     return 0;
7058
7059   set<const SMDS_MeshNode*> nodeSet;
7060
7061   // get simple seq of nodes
7062   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7063   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7064   int iSimple = 0, nbUnique = 0;
7065
7066   simpleNodes[iSimple++] = faceNodes[0];
7067   nbUnique++;
7068   for (int iCur = 1; iCur < nbNodes; iCur++) {
7069     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7070       simpleNodes[iSimple++] = faceNodes[iCur];
7071       if (nodeSet.insert( faceNodes[iCur] ).second)
7072         nbUnique++;
7073     }
7074   }
7075   int nbSimple = iSimple;
7076   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7077     nbSimple--;
7078     iSimple--;
7079   }
7080
7081   if (nbUnique < 3)
7082     return 0;
7083
7084   // separate loops
7085   int nbNew = 0;
7086   bool foundLoop = (nbSimple > nbUnique);
7087   while (foundLoop) {
7088     foundLoop = false;
7089     set<const SMDS_MeshNode*> loopSet;
7090     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7091       const SMDS_MeshNode* n = simpleNodes[iSimple];
7092       if (!loopSet.insert( n ).second) {
7093         foundLoop = true;
7094
7095         // separate loop
7096         int iC = 0, curLast = iSimple;
7097         for (; iC < curLast; iC++) {
7098           if (simpleNodes[iC] == n) break;
7099         }
7100         int loopLen = curLast - iC;
7101         if (loopLen > 2) {
7102           // create sub-element
7103           nbNew++;
7104           quantities.push_back(loopLen);
7105           for (; iC < curLast; iC++) {
7106             poly_nodes.push_back(simpleNodes[iC]);
7107           }
7108         }
7109         // shift the rest nodes (place from the first loop position)
7110         for (iC = curLast + 1; iC < nbSimple; iC++) {
7111           simpleNodes[iC - loopLen] = simpleNodes[iC];
7112         }
7113         nbSimple -= loopLen;
7114         iSimple -= loopLen;
7115       }
7116     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7117   } // while (foundLoop)
7118
7119   if (iSimple > 2) {
7120     nbNew++;
7121     quantities.push_back(iSimple);
7122     for (int i = 0; i < iSimple; i++)
7123       poly_nodes.push_back(simpleNodes[i]);
7124   }
7125
7126   return nbNew;
7127 }
7128
7129 //=======================================================================
7130 //function : MergeNodes
7131 //purpose  : In each group, the cdr of nodes are substituted by the first one
7132 //           in all elements.
7133 //=======================================================================
7134
7135 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7136 {
7137   MESSAGE("MergeNodes");
7138   myLastCreatedElems.Clear();
7139   myLastCreatedNodes.Clear();
7140
7141   SMESHDS_Mesh* aMesh = GetMeshDS();
7142
7143   TNodeNodeMap nodeNodeMap; // node to replace - new node
7144   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7145   list< int > rmElemIds, rmNodeIds;
7146
7147   // Fill nodeNodeMap and elems
7148
7149   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7150   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7151     list<const SMDS_MeshNode*>& nodes = *grIt;
7152     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7153     const SMDS_MeshNode* nToKeep = *nIt;
7154     //MESSAGE("node to keep " << nToKeep->GetID());
7155     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7156       const SMDS_MeshNode* nToRemove = *nIt;
7157       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7158       if ( nToRemove != nToKeep ) {
7159         //MESSAGE("  node to remove " << nToRemove->GetID());
7160         rmNodeIds.push_back( nToRemove->GetID() );
7161         AddToSameGroups( nToKeep, nToRemove, aMesh );
7162         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7163         // after MergeNodes() w/o creating node in place of merged ones.
7164         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7165         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7166           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7167             sm->SetIsAlwaysComputed( true );
7168       }
7169
7170       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7171       while ( invElemIt->more() ) {
7172         const SMDS_MeshElement* elem = invElemIt->next();
7173         elems.insert(elem);
7174       }
7175     }
7176   }
7177   // Change element nodes or remove an element
7178
7179   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7180   for ( ; eIt != elems.end(); eIt++ ) {
7181     const SMDS_MeshElement* elem = *eIt;
7182     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7183     int nbNodes = elem->NbNodes();
7184     int aShapeId = FindShape( elem );
7185
7186     set<const SMDS_MeshNode*> nodeSet;
7187     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7188     int iUnique = 0, iCur = 0, nbRepl = 0;
7189     vector<int> iRepl( nbNodes );
7190
7191     // get new seq of nodes
7192     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7193     while ( itN->more() ) {
7194       const SMDS_MeshNode* n =
7195         static_cast<const SMDS_MeshNode*>( itN->next() );
7196
7197       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7198       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7199         n = (*nnIt).second;
7200         // BUG 0020185: begin
7201         {
7202           bool stopRecur = false;
7203           set<const SMDS_MeshNode*> nodesRecur;
7204           nodesRecur.insert(n);
7205           while (!stopRecur) {
7206             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7207             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7208               n = (*nnIt_i).second;
7209               if (!nodesRecur.insert(n).second) {
7210                 // error: recursive dependancy
7211                 stopRecur = true;
7212               }
7213             }
7214             else
7215               stopRecur = true;
7216           }
7217         }
7218         // BUG 0020185: end
7219       }
7220       curNodes[ iCur ] = n;
7221       bool isUnique = nodeSet.insert( n ).second;
7222       if ( isUnique )
7223         uniqueNodes[ iUnique++ ] = n;
7224       else
7225         iRepl[ nbRepl++ ] = iCur;
7226       iCur++;
7227     }
7228
7229     // Analyse element topology after replacement
7230
7231     bool isOk = true;
7232     int nbUniqueNodes = nodeSet.size();
7233     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7234     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7235       // Polygons and Polyhedral volumes
7236       if (elem->IsPoly()) {
7237
7238         if (elem->GetType() == SMDSAbs_Face) {
7239           // Polygon
7240           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7241           int inode = 0;
7242           for (; inode < nbNodes; inode++) {
7243             face_nodes[inode] = curNodes[inode];
7244           }
7245
7246           vector<const SMDS_MeshNode *> polygons_nodes;
7247           vector<int> quantities;
7248           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7249           if (nbNew > 0) {
7250             inode = 0;
7251             for (int iface = 0; iface < nbNew; iface++) {
7252               int nbNodes = quantities[iface];
7253               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7254               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7255                 poly_nodes[ii] = polygons_nodes[inode];
7256               }
7257               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7258               myLastCreatedElems.Append(newElem);
7259               if (aShapeId)
7260                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7261             }
7262
7263             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7264             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7265             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7266             int quid =0;
7267             if (nbNew > 0) quid = nbNew - 1;
7268             vector<int> newquant(quantities.begin()+quid, quantities.end());
7269             const SMDS_MeshElement* newElem = 0;
7270             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7271             myLastCreatedElems.Append(newElem);
7272             if ( aShapeId && newElem )
7273               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7274             rmElemIds.push_back(elem->GetID());
7275           }
7276           else {
7277             rmElemIds.push_back(elem->GetID());
7278           }
7279
7280         }
7281         else if (elem->GetType() == SMDSAbs_Volume) {
7282           // Polyhedral volume
7283           if (nbUniqueNodes < 4) {
7284             rmElemIds.push_back(elem->GetID());
7285           }
7286           else {
7287             // each face has to be analyzed in order to check volume validity
7288             const SMDS_VtkVolume* aPolyedre =
7289               dynamic_cast<const SMDS_VtkVolume*>( elem );
7290             if (aPolyedre) {
7291               int nbFaces = aPolyedre->NbFaces();
7292
7293               vector<const SMDS_MeshNode *> poly_nodes;
7294               vector<int> quantities;
7295
7296               for (int iface = 1; iface <= nbFaces; iface++) {
7297                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7298                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7299
7300                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7301                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7302                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7303                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7304                     faceNode = (*nnIt).second;
7305                   }
7306                   faceNodes[inode - 1] = faceNode;
7307                 }
7308
7309                 SimplifyFace(faceNodes, poly_nodes, quantities);
7310               }
7311
7312               if (quantities.size() > 3) {
7313                 // to be done: remove coincident faces
7314               }
7315
7316               if (quantities.size() > 3)
7317                 {
7318                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7319                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7320                   const SMDS_MeshElement* newElem = 0;
7321                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7322                   myLastCreatedElems.Append(newElem);
7323                   if ( aShapeId && newElem )
7324                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7325                   rmElemIds.push_back(elem->GetID());
7326                 }
7327             }
7328             else {
7329               rmElemIds.push_back(elem->GetID());
7330             }
7331           }
7332         }
7333         else {
7334         }
7335
7336         continue;
7337       } // poly element
7338
7339       // Regular elements
7340       // TODO not all the possible cases are solved. Find something more generic?
7341       switch ( nbNodes ) {
7342       case 2: ///////////////////////////////////// EDGE
7343         isOk = false; break;
7344       case 3: ///////////////////////////////////// TRIANGLE
7345         isOk = false; break;
7346       case 4:
7347         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7348           isOk = false;
7349         else { //////////////////////////////////// QUADRANGLE
7350           if ( nbUniqueNodes < 3 )
7351             isOk = false;
7352           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7353             isOk = false; // opposite nodes stick
7354           //MESSAGE("isOk " << isOk);
7355         }
7356         break;
7357       case 6: ///////////////////////////////////// PENTAHEDRON
7358         if ( nbUniqueNodes == 4 ) {
7359           // ---------------------------------> tetrahedron
7360           if (nbRepl == 3 &&
7361               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7362             // all top nodes stick: reverse a bottom
7363             uniqueNodes[ 0 ] = curNodes [ 1 ];
7364             uniqueNodes[ 1 ] = curNodes [ 0 ];
7365           }
7366           else if (nbRepl == 3 &&
7367                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7368             // all bottom nodes stick: set a top before
7369             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7370             uniqueNodes[ 0 ] = curNodes [ 3 ];
7371             uniqueNodes[ 1 ] = curNodes [ 4 ];
7372             uniqueNodes[ 2 ] = curNodes [ 5 ];
7373           }
7374           else if (nbRepl == 4 &&
7375                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7376             // a lateral face turns into a line: reverse a bottom
7377             uniqueNodes[ 0 ] = curNodes [ 1 ];
7378             uniqueNodes[ 1 ] = curNodes [ 0 ];
7379           }
7380           else
7381             isOk = false;
7382         }
7383         else if ( nbUniqueNodes == 5 ) {
7384           // PENTAHEDRON --------------------> 2 tetrahedrons
7385           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7386             // a bottom node sticks with a linked top one
7387             // 1.
7388             SMDS_MeshElement* newElem =
7389               aMesh->AddVolume(curNodes[ 3 ],
7390                                curNodes[ 4 ],
7391                                curNodes[ 5 ],
7392                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7393             myLastCreatedElems.Append(newElem);
7394             if ( aShapeId )
7395               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7396             // 2. : reverse a bottom
7397             uniqueNodes[ 0 ] = curNodes [ 1 ];
7398             uniqueNodes[ 1 ] = curNodes [ 0 ];
7399             nbUniqueNodes = 4;
7400           }
7401           else
7402             isOk = false;
7403         }
7404         else
7405           isOk = false;
7406         break;
7407       case 8: {
7408         if(elem->IsQuadratic()) { // Quadratic quadrangle
7409           //   1    5    2
7410           //    +---+---+
7411           //    |       |
7412           //    |       |
7413           //   4+       +6
7414           //    |       |
7415           //    |       |
7416           //    +---+---+
7417           //   0    7    3
7418           isOk = false;
7419           if(nbRepl==2) {
7420             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7421           }
7422           if(nbRepl==3) {
7423             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7424             nbUniqueNodes = 6;
7425             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7426               uniqueNodes[0] = curNodes[0];
7427               uniqueNodes[1] = curNodes[2];
7428               uniqueNodes[2] = curNodes[3];
7429               uniqueNodes[3] = curNodes[5];
7430               uniqueNodes[4] = curNodes[6];
7431               uniqueNodes[5] = curNodes[7];
7432               isOk = true;
7433             }
7434             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7435               uniqueNodes[0] = curNodes[0];
7436               uniqueNodes[1] = curNodes[1];
7437               uniqueNodes[2] = curNodes[2];
7438               uniqueNodes[3] = curNodes[4];
7439               uniqueNodes[4] = curNodes[5];
7440               uniqueNodes[5] = curNodes[6];
7441               isOk = true;
7442             }
7443             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7444               uniqueNodes[0] = curNodes[1];
7445               uniqueNodes[1] = curNodes[2];
7446               uniqueNodes[2] = curNodes[3];
7447               uniqueNodes[3] = curNodes[5];
7448               uniqueNodes[4] = curNodes[6];
7449               uniqueNodes[5] = curNodes[0];
7450               isOk = true;
7451             }
7452             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7453               uniqueNodes[0] = curNodes[0];
7454               uniqueNodes[1] = curNodes[1];
7455               uniqueNodes[2] = curNodes[3];
7456               uniqueNodes[3] = curNodes[4];
7457               uniqueNodes[4] = curNodes[6];
7458               uniqueNodes[5] = curNodes[7];
7459               isOk = true;
7460             }
7461             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7462               uniqueNodes[0] = curNodes[0];
7463               uniqueNodes[1] = curNodes[2];
7464               uniqueNodes[2] = curNodes[3];
7465               uniqueNodes[3] = curNodes[1];
7466               uniqueNodes[4] = curNodes[6];
7467               uniqueNodes[5] = curNodes[7];
7468               isOk = true;
7469             }
7470             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7471               uniqueNodes[0] = curNodes[0];
7472               uniqueNodes[1] = curNodes[1];
7473               uniqueNodes[2] = curNodes[2];
7474               uniqueNodes[3] = curNodes[4];
7475               uniqueNodes[4] = curNodes[5];
7476               uniqueNodes[5] = curNodes[7];
7477               isOk = true;
7478             }
7479             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7480               uniqueNodes[0] = curNodes[0];
7481               uniqueNodes[1] = curNodes[1];
7482               uniqueNodes[2] = curNodes[3];
7483               uniqueNodes[3] = curNodes[4];
7484               uniqueNodes[4] = curNodes[2];
7485               uniqueNodes[5] = curNodes[7];
7486               isOk = true;
7487             }
7488             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7489               uniqueNodes[0] = curNodes[0];
7490               uniqueNodes[1] = curNodes[1];
7491               uniqueNodes[2] = curNodes[2];
7492               uniqueNodes[3] = curNodes[4];
7493               uniqueNodes[4] = curNodes[5];
7494               uniqueNodes[5] = curNodes[3];
7495               isOk = true;
7496             }
7497           }
7498           if(nbRepl==4) {
7499             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7500           }
7501           if(nbRepl==5) {
7502             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7503           }
7504           break;
7505         }
7506         //////////////////////////////////// HEXAHEDRON
7507         isOk = false;
7508         SMDS_VolumeTool hexa (elem);
7509         hexa.SetExternalNormal();
7510         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7511           //////////////////////// HEX ---> 1 tetrahedron
7512           for ( int iFace = 0; iFace < 6; iFace++ ) {
7513             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7514             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7515                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7516                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7517               // one face turns into a point ...
7518               int iOppFace = hexa.GetOppFaceIndex( iFace );
7519               ind = hexa.GetFaceNodesIndices( iOppFace );
7520               int nbStick = 0;
7521               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7522                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7523                   nbStick++;
7524               }
7525               if ( nbStick == 1 ) {
7526                 // ... and the opposite one - into a triangle.
7527                 // set a top node
7528                 ind = hexa.GetFaceNodesIndices( iFace );
7529                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7530                 isOk = true;
7531               }
7532               break;
7533             }
7534           }
7535         }
7536         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7537           //////////////////////// HEX ---> 1 prism
7538           int nbTria = 0, iTria[3];
7539           const int *ind; // indices of face nodes
7540           // look for triangular faces
7541           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7542             ind = hexa.GetFaceNodesIndices( iFace );
7543             TIDSortedNodeSet faceNodes;
7544             for ( iCur = 0; iCur < 4; iCur++ )
7545               faceNodes.insert( curNodes[ind[iCur]] );
7546             if ( faceNodes.size() == 3 )
7547               iTria[ nbTria++ ] = iFace;
7548           }
7549           // check if triangles are opposite
7550           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7551           {
7552             isOk = true;
7553             // set nodes of the bottom triangle
7554             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7555             vector<int> indB;
7556             for ( iCur = 0; iCur < 4; iCur++ )
7557               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7558                 indB.push_back( ind[iCur] );
7559             if ( !hexa.IsForward() )
7560               std::swap( indB[0], indB[2] );
7561             for ( iCur = 0; iCur < 3; iCur++ )
7562               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7563             // set nodes of the top triangle
7564             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7565             for ( iCur = 0; iCur < 3; ++iCur )
7566               for ( int j = 0; j < 4; ++j )
7567                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7568                 {
7569                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7570                   break;
7571                 }
7572           }
7573           break;
7574         }
7575         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7576           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7577           for ( int iFace = 0; iFace < 6; iFace++ ) {
7578             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7579             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7580                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7581                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7582               // one face turns into a point ...
7583               int iOppFace = hexa.GetOppFaceIndex( iFace );
7584               ind = hexa.GetFaceNodesIndices( iOppFace );
7585               int nbStick = 0;
7586               iUnique = 2;  // reverse a tetrahedron 1 bottom
7587               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7588                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7589                   nbStick++;
7590                 else if ( iUnique >= 0 )
7591                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7592               }
7593               if ( nbStick == 0 ) {
7594                 // ... and the opposite one is a quadrangle
7595                 // set a top node
7596                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7597                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7598                 nbUniqueNodes = 4;
7599                 // tetrahedron 2
7600                 SMDS_MeshElement* newElem =
7601                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7602                                    curNodes[ind[ 3 ]],
7603                                    curNodes[ind[ 2 ]],
7604                                    curNodes[indTop[ 0 ]]);
7605                 myLastCreatedElems.Append(newElem);
7606                 if ( aShapeId )
7607                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7608                 isOk = true;
7609               }
7610               break;
7611             }
7612           }
7613         }
7614         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7615           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7616           // find indices of quad and tri faces
7617           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7618           for ( iFace = 0; iFace < 6; iFace++ ) {
7619             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7620             nodeSet.clear();
7621             for ( iCur = 0; iCur < 4; iCur++ )
7622               nodeSet.insert( curNodes[ind[ iCur ]] );
7623             nbUniqueNodes = nodeSet.size();
7624             if ( nbUniqueNodes == 3 )
7625               iTriFace[ nbTri++ ] = iFace;
7626             else if ( nbUniqueNodes == 4 )
7627               iQuadFace[ nbQuad++ ] = iFace;
7628           }
7629           if (nbQuad == 2 && nbTri == 4 &&
7630               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7631             // 2 opposite quadrangles stuck with a diagonal;
7632             // sample groups of merged indices: (0-4)(2-6)
7633             // --------------------------------------------> 2 tetrahedrons
7634             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7635             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7636             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7637             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7638                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7639               // stuck with 0-2 diagonal
7640               i0  = ind1[ 3 ];
7641               i1d = ind1[ 0 ];
7642               i2  = ind1[ 1 ];
7643               i3d = ind1[ 2 ];
7644               i0t = ind2[ 1 ];
7645               i2t = ind2[ 3 ];
7646             }
7647             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7648                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7649               // stuck with 1-3 diagonal
7650               i0  = ind1[ 0 ];
7651               i1d = ind1[ 1 ];
7652               i2  = ind1[ 2 ];
7653               i3d = ind1[ 3 ];
7654               i0t = ind2[ 0 ];
7655               i2t = ind2[ 1 ];
7656             }
7657             else {
7658               ASSERT(0);
7659             }
7660             // tetrahedron 1
7661             uniqueNodes[ 0 ] = curNodes [ i0 ];
7662             uniqueNodes[ 1 ] = curNodes [ i1d ];
7663             uniqueNodes[ 2 ] = curNodes [ i3d ];
7664             uniqueNodes[ 3 ] = curNodes [ i0t ];
7665             nbUniqueNodes = 4;
7666             // tetrahedron 2
7667             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7668                                                          curNodes[ i2 ],
7669                                                          curNodes[ i3d ],
7670                                                          curNodes[ i2t ]);
7671             myLastCreatedElems.Append(newElem);
7672             if ( aShapeId )
7673               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7674             isOk = true;
7675           }
7676           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7677                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7678             // --------------------------------------------> prism
7679             // find 2 opposite triangles
7680             nbUniqueNodes = 6;
7681             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7682               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7683                 // find indices of kept and replaced nodes
7684                 // and fill unique nodes of 2 opposite triangles
7685                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7686                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7687                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7688                 // fill unique nodes
7689                 iUnique = 0;
7690                 isOk = true;
7691                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7692                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7693                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7694                   if ( n == nInit ) {
7695                     // iCur of a linked node of the opposite face (make normals co-directed):
7696                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7697                     // check that correspondent corners of triangles are linked
7698                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7699                       isOk = false;
7700                     else {
7701                       uniqueNodes[ iUnique ] = n;
7702                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7703                       iUnique++;
7704                     }
7705                   }
7706                 }
7707                 break;
7708               }
7709             }
7710           }
7711         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7712         else
7713         {
7714           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7715         }
7716         break;
7717       } // HEXAHEDRON
7718
7719       default:
7720         isOk = false;
7721       } // switch ( nbNodes )
7722
7723     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7724
7725     if ( isOk ) { // the elem remains valid after sticking nodes
7726       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7727       {
7728         // Change nodes of polyedre
7729         const SMDS_VtkVolume* aPolyedre =
7730           dynamic_cast<const SMDS_VtkVolume*>( elem );
7731         if (aPolyedre) {
7732           int nbFaces = aPolyedre->NbFaces();
7733
7734           vector<const SMDS_MeshNode *> poly_nodes;
7735           vector<int> quantities (nbFaces);
7736
7737           for (int iface = 1; iface <= nbFaces; iface++) {
7738             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7739             quantities[iface - 1] = nbFaceNodes;
7740
7741             for (inode = 1; inode <= nbFaceNodes; inode++) {
7742               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7743
7744               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7745               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7746                 curNode = (*nnIt).second;
7747               }
7748               poly_nodes.push_back(curNode);
7749             }
7750           }
7751           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7752         }
7753       }
7754       else // replace non-polyhedron elements
7755       {
7756         const SMDSAbs_ElementType etyp = elem->GetType();
7757         const int elemId               = elem->GetID();
7758         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7759         uniqueNodes.resize(nbUniqueNodes);
7760
7761         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7762
7763         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7764         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7765         if ( sm && newElem )
7766           sm->AddElement( newElem );
7767         if ( elem != newElem )
7768           ReplaceElemInGroups( elem, newElem, aMesh );
7769       }
7770     }
7771     else {
7772       // Remove invalid regular element or invalid polygon
7773       rmElemIds.push_back( elem->GetID() );
7774     }
7775
7776   } // loop on elements
7777
7778   // Remove bad elements, then equal nodes (order important)
7779
7780   Remove( rmElemIds, false );
7781   Remove( rmNodeIds, true );
7782
7783 }
7784
7785
7786 // ========================================================
7787 // class   : SortableElement
7788 // purpose : allow sorting elements basing on their nodes
7789 // ========================================================
7790 class SortableElement : public set <const SMDS_MeshElement*>
7791 {
7792 public:
7793
7794   SortableElement( const SMDS_MeshElement* theElem )
7795   {
7796     myElem = theElem;
7797     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7798     while ( nodeIt->more() )
7799       this->insert( nodeIt->next() );
7800   }
7801
7802   const SMDS_MeshElement* Get() const
7803   { return myElem; }
7804
7805   void Set(const SMDS_MeshElement* e) const
7806   { myElem = e; }
7807
7808
7809 private:
7810   mutable const SMDS_MeshElement* myElem;
7811 };
7812
7813 //=======================================================================
7814 //function : FindEqualElements
7815 //purpose  : Return list of group of elements built on the same nodes.
7816 //           Search among theElements or in the whole mesh if theElements is empty
7817 //=======================================================================
7818
7819 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7820                                          TListOfListOfElementsID & theGroupsOfElementsID)
7821 {
7822   myLastCreatedElems.Clear();
7823   myLastCreatedNodes.Clear();
7824
7825   typedef map< SortableElement, int > TMapOfNodeSet;
7826   typedef list<int> TGroupOfElems;
7827
7828   if ( theElements.empty() )
7829   { // get all elements in the mesh
7830     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7831     while ( eIt->more() )
7832       theElements.insert( theElements.end(), eIt->next());
7833   }
7834
7835   vector< TGroupOfElems > arrayOfGroups;
7836   TGroupOfElems groupOfElems;
7837   TMapOfNodeSet mapOfNodeSet;
7838
7839   TIDSortedElemSet::iterator elemIt = theElements.begin();
7840   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7841     const SMDS_MeshElement* curElem = *elemIt;
7842     SortableElement SE(curElem);
7843     int ind = -1;
7844     // check uniqueness
7845     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7846     if( !(pp.second) ) {
7847       TMapOfNodeSet::iterator& itSE = pp.first;
7848       ind = (*itSE).second;
7849       arrayOfGroups[ind].push_back(curElem->GetID());
7850     }
7851     else {
7852       groupOfElems.clear();
7853       groupOfElems.push_back(curElem->GetID());
7854       arrayOfGroups.push_back(groupOfElems);
7855       i++;
7856     }
7857   }
7858
7859   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7860   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7861     groupOfElems = *groupIt;
7862     if ( groupOfElems.size() > 1 ) {
7863       groupOfElems.sort();
7864       theGroupsOfElementsID.push_back(groupOfElems);
7865     }
7866   }
7867 }
7868
7869 //=======================================================================
7870 //function : MergeElements
7871 //purpose  : In each given group, substitute all elements by the first one.
7872 //=======================================================================
7873
7874 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7875 {
7876   myLastCreatedElems.Clear();
7877   myLastCreatedNodes.Clear();
7878
7879   typedef list<int> TListOfIDs;
7880   TListOfIDs rmElemIds; // IDs of elems to remove
7881
7882   SMESHDS_Mesh* aMesh = GetMeshDS();
7883
7884   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7885   while ( groupsIt != theGroupsOfElementsID.end() ) {
7886     TListOfIDs& aGroupOfElemID = *groupsIt;
7887     aGroupOfElemID.sort();
7888     int elemIDToKeep = aGroupOfElemID.front();
7889     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7890     aGroupOfElemID.pop_front();
7891     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7892     while ( idIt != aGroupOfElemID.end() ) {
7893       int elemIDToRemove = *idIt;
7894       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7895       // add the kept element in groups of removed one (PAL15188)
7896       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7897       rmElemIds.push_back( elemIDToRemove );
7898       ++idIt;
7899     }
7900     ++groupsIt;
7901   }
7902
7903   Remove( rmElemIds, false );
7904 }
7905
7906 //=======================================================================
7907 //function : MergeEqualElements
7908 //purpose  : Remove all but one of elements built on the same nodes.
7909 //=======================================================================
7910
7911 void SMESH_MeshEditor::MergeEqualElements()
7912 {
7913   TIDSortedElemSet aMeshElements; /* empty input ==
7914                                      to merge equal elements in the whole mesh */
7915   TListOfListOfElementsID aGroupsOfElementsID;
7916   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7917   MergeElements(aGroupsOfElementsID);
7918 }
7919
7920 //=======================================================================
7921 //function : findAdjacentFace
7922 //purpose  :
7923 //=======================================================================
7924
7925 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7926                                                 const SMDS_MeshNode* n2,
7927                                                 const SMDS_MeshElement* elem)
7928 {
7929   TIDSortedElemSet elemSet, avoidSet;
7930   if ( elem )
7931     avoidSet.insert ( elem );
7932   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7933 }
7934
7935 //=======================================================================
7936 //function : FindFreeBorder
7937 //purpose  :
7938 //=======================================================================
7939
7940 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7941
7942 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7943                                        const SMDS_MeshNode*             theSecondNode,
7944                                        const SMDS_MeshNode*             theLastNode,
7945                                        list< const SMDS_MeshNode* > &   theNodes,
7946                                        list< const SMDS_MeshElement* >& theFaces)
7947 {
7948   if ( !theFirstNode || !theSecondNode )
7949     return false;
7950   // find border face between theFirstNode and theSecondNode
7951   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7952   if ( !curElem )
7953     return false;
7954
7955   theFaces.push_back( curElem );
7956   theNodes.push_back( theFirstNode );
7957   theNodes.push_back( theSecondNode );
7958
7959   //vector<const SMDS_MeshNode*> nodes;
7960   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7961   TIDSortedElemSet foundElems;
7962   bool needTheLast = ( theLastNode != 0 );
7963
7964   while ( nStart != theLastNode ) {
7965     if ( nStart == theFirstNode )
7966       return !needTheLast;
7967
7968     // find all free border faces sharing form nStart
7969
7970     list< const SMDS_MeshElement* > curElemList;
7971     list< const SMDS_MeshNode* > nStartList;
7972     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7973     while ( invElemIt->more() ) {
7974       const SMDS_MeshElement* e = invElemIt->next();
7975       if ( e == curElem || foundElems.insert( e ).second ) {
7976         // get nodes
7977         int iNode = 0, nbNodes = e->NbNodes();
7978         //const SMDS_MeshNode* nodes[nbNodes+1];
7979         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7980
7981         if(e->IsQuadratic()) {
7982           const SMDS_VtkFace* F =
7983             dynamic_cast<const SMDS_VtkFace*>(e);
7984           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7985           // use special nodes iterator
7986           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7987           while( anIter->more() ) {
7988             nodes[ iNode++ ] = cast2Node(anIter->next());
7989           }
7990         }
7991         else {
7992           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7993           while ( nIt->more() )
7994             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7995         }
7996         nodes[ iNode ] = nodes[ 0 ];
7997         // check 2 links
7998         for ( iNode = 0; iNode < nbNodes; iNode++ )
7999           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8000                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8001               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8002           {
8003             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8004             curElemList.push_back( e );
8005           }
8006       }
8007     }
8008     // analyse the found
8009
8010     int nbNewBorders = curElemList.size();
8011     if ( nbNewBorders == 0 ) {
8012       // no free border furthermore
8013       return !needTheLast;
8014     }
8015     else if ( nbNewBorders == 1 ) {
8016       // one more element found
8017       nIgnore = nStart;
8018       nStart = nStartList.front();
8019       curElem = curElemList.front();
8020       theFaces.push_back( curElem );
8021       theNodes.push_back( nStart );
8022     }
8023     else {
8024       // several continuations found
8025       list< const SMDS_MeshElement* >::iterator curElemIt;
8026       list< const SMDS_MeshNode* >::iterator nStartIt;
8027       // check if one of them reached the last node
8028       if ( needTheLast ) {
8029         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8030              curElemIt!= curElemList.end();
8031              curElemIt++, nStartIt++ )
8032           if ( *nStartIt == theLastNode ) {
8033             theFaces.push_back( *curElemIt );
8034             theNodes.push_back( *nStartIt );
8035             return true;
8036           }
8037       }
8038       // find the best free border by the continuations
8039       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8040       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8041       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8042            curElemIt!= curElemList.end();
8043            curElemIt++, nStartIt++ )
8044       {
8045         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8046         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8047         // find one more free border
8048         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8049           cNL->clear();
8050           cFL->clear();
8051         }
8052         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8053           // choice: clear a worse one
8054           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8055           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8056           contNodes[ iWorse ].clear();
8057           contFaces[ iWorse ].clear();
8058         }
8059       }
8060       if ( contNodes[0].empty() && contNodes[1].empty() )
8061         return false;
8062
8063       // append the best free border
8064       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8065       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8066       theNodes.pop_back(); // remove nIgnore
8067       theNodes.pop_back(); // remove nStart
8068       theFaces.pop_back(); // remove curElem
8069       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8070       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8071       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8072       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8073       return true;
8074
8075     } // several continuations found
8076   } // while ( nStart != theLastNode )
8077
8078   return true;
8079 }
8080
8081 //=======================================================================
8082 //function : CheckFreeBorderNodes
8083 //purpose  : Return true if the tree nodes are on a free border
8084 //=======================================================================
8085
8086 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8087                                             const SMDS_MeshNode* theNode2,
8088                                             const SMDS_MeshNode* theNode3)
8089 {
8090   list< const SMDS_MeshNode* > nodes;
8091   list< const SMDS_MeshElement* > faces;
8092   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8093 }
8094
8095 //=======================================================================
8096 //function : SewFreeBorder
8097 //purpose  :
8098 //=======================================================================
8099
8100 SMESH_MeshEditor::Sew_Error
8101 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8102                                  const SMDS_MeshNode* theBordSecondNode,
8103                                  const SMDS_MeshNode* theBordLastNode,
8104                                  const SMDS_MeshNode* theSideFirstNode,
8105                                  const SMDS_MeshNode* theSideSecondNode,
8106                                  const SMDS_MeshNode* theSideThirdNode,
8107                                  const bool           theSideIsFreeBorder,
8108                                  const bool           toCreatePolygons,
8109                                  const bool           toCreatePolyedrs)
8110 {
8111   myLastCreatedElems.Clear();
8112   myLastCreatedNodes.Clear();
8113
8114   MESSAGE("::SewFreeBorder()");
8115   Sew_Error aResult = SEW_OK;
8116
8117   // ====================================
8118   //    find side nodes and elements
8119   // ====================================
8120
8121   list< const SMDS_MeshNode* > nSide[ 2 ];
8122   list< const SMDS_MeshElement* > eSide[ 2 ];
8123   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8124   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8125
8126   // Free border 1
8127   // --------------
8128   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8129                       nSide[0], eSide[0])) {
8130     MESSAGE(" Free Border 1 not found " );
8131     aResult = SEW_BORDER1_NOT_FOUND;
8132   }
8133   if (theSideIsFreeBorder) {
8134     // Free border 2
8135     // --------------
8136     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8137                         nSide[1], eSide[1])) {
8138       MESSAGE(" Free Border 2 not found " );
8139       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8140     }
8141   }
8142   if ( aResult != SEW_OK )
8143     return aResult;
8144
8145   if (!theSideIsFreeBorder) {
8146     // Side 2
8147     // --------------
8148
8149     // -------------------------------------------------------------------------
8150     // Algo:
8151     // 1. If nodes to merge are not coincident, move nodes of the free border
8152     //    from the coord sys defined by the direction from the first to last
8153     //    nodes of the border to the correspondent sys of the side 2
8154     // 2. On the side 2, find the links most co-directed with the correspondent
8155     //    links of the free border
8156     // -------------------------------------------------------------------------
8157
8158     // 1. Since sewing may break if there are volumes to split on the side 2,
8159     //    we wont move nodes but just compute new coordinates for them
8160     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8161     TNodeXYZMap nBordXYZ;
8162     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8163     list< const SMDS_MeshNode* >::iterator nBordIt;
8164
8165     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8166     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8167     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8168     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8169     double tol2 = 1.e-8;
8170     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8171     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8172       // Need node movement.
8173
8174       // find X and Z axes to create trsf
8175       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8176       gp_Vec X = Zs ^ Zb;
8177       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8178         // Zb || Zs
8179         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8180
8181       // coord systems
8182       gp_Ax3 toBordAx( Pb1, Zb, X );
8183       gp_Ax3 fromSideAx( Ps1, Zs, X );
8184       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8185       // set trsf
8186       gp_Trsf toBordSys, fromSide2Sys;
8187       toBordSys.SetTransformation( toBordAx );
8188       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8189       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8190
8191       // move
8192       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8193         const SMDS_MeshNode* n = *nBordIt;
8194         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8195         toBordSys.Transforms( xyz );
8196         fromSide2Sys.Transforms( xyz );
8197         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8198       }
8199     }
8200     else {
8201       // just insert nodes XYZ in the nBordXYZ map
8202       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8203         const SMDS_MeshNode* n = *nBordIt;
8204         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8205       }
8206     }
8207
8208     // 2. On the side 2, find the links most co-directed with the correspondent
8209     //    links of the free border
8210
8211     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8212     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8213     sideNodes.push_back( theSideFirstNode );
8214
8215     bool hasVolumes = false;
8216     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8217     set<long> foundSideLinkIDs, checkedLinkIDs;
8218     SMDS_VolumeTool volume;
8219     //const SMDS_MeshNode* faceNodes[ 4 ];
8220
8221     const SMDS_MeshNode*    sideNode;
8222     const SMDS_MeshElement* sideElem;
8223     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8224     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8225     nBordIt = bordNodes.begin();
8226     nBordIt++;
8227     // border node position and border link direction to compare with
8228     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8229     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8230     // choose next side node by link direction or by closeness to
8231     // the current border node:
8232     bool searchByDir = ( *nBordIt != theBordLastNode );
8233     do {
8234       // find the next node on the Side 2
8235       sideNode = 0;
8236       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8237       long linkID;
8238       checkedLinkIDs.clear();
8239       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8240
8241       // loop on inverse elements of current node (prevSideNode) on the Side 2
8242       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8243       while ( invElemIt->more() )
8244       {
8245         const SMDS_MeshElement* elem = invElemIt->next();
8246         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8247         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8248         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8249         bool isVolume = volume.Set( elem );
8250         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8251         if ( isVolume ) // --volume
8252           hasVolumes = true;
8253         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8254           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8255           if(elem->IsQuadratic()) {
8256             const SMDS_VtkFace* F =
8257               dynamic_cast<const SMDS_VtkFace*>(elem);
8258             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8259             // use special nodes iterator
8260             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8261             while( anIter->more() ) {
8262               nodes[ iNode ] = cast2Node(anIter->next());
8263               if ( nodes[ iNode++ ] == prevSideNode )
8264                 iPrevNode = iNode - 1;
8265             }
8266           }
8267           else {
8268             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8269             while ( nIt->more() ) {
8270               nodes[ iNode ] = cast2Node( nIt->next() );
8271               if ( nodes[ iNode++ ] == prevSideNode )
8272                 iPrevNode = iNode - 1;
8273             }
8274           }
8275           // there are 2 links to check
8276           nbNodes = 2;
8277         }
8278         else // --edge
8279           continue;
8280         // loop on links, to be precise, on the second node of links
8281         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8282           const SMDS_MeshNode* n = nodes[ iNode ];
8283           if ( isVolume ) {
8284             if ( !volume.IsLinked( n, prevSideNode ))
8285               continue;
8286           }
8287           else {
8288             if ( iNode ) // a node before prevSideNode
8289               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8290             else         // a node after prevSideNode
8291               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8292           }
8293           // check if this link was already used
8294           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8295           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8296           if (!isJustChecked &&
8297               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8298           {
8299             // test a link geometrically
8300             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8301             bool linkIsBetter = false;
8302             double dot = 0.0, dist = 0.0;
8303             if ( searchByDir ) { // choose most co-directed link
8304               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8305               linkIsBetter = ( dot > maxDot );
8306             }
8307             else { // choose link with the node closest to bordPos
8308               dist = ( nextXYZ - bordPos ).SquareModulus();
8309               linkIsBetter = ( dist < minDist );
8310             }
8311             if ( linkIsBetter ) {
8312               maxDot = dot;
8313               minDist = dist;
8314               linkID = iLink;
8315               sideNode = n;
8316               sideElem = elem;
8317             }
8318           }
8319         }
8320       } // loop on inverse elements of prevSideNode
8321
8322       if ( !sideNode ) {
8323         MESSAGE(" Cant find path by links of the Side 2 ");
8324         return SEW_BAD_SIDE_NODES;
8325       }
8326       sideNodes.push_back( sideNode );
8327       sideElems.push_back( sideElem );
8328       foundSideLinkIDs.insert ( linkID );
8329       prevSideNode = sideNode;
8330
8331       if ( *nBordIt == theBordLastNode )
8332         searchByDir = false;
8333       else {
8334         // find the next border link to compare with
8335         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8336         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8337         // move to next border node if sideNode is before forward border node (bordPos)
8338         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8339           prevBordNode = *nBordIt;
8340           nBordIt++;
8341           bordPos = nBordXYZ[ *nBordIt ];
8342           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8343           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8344         }
8345       }
8346     }
8347     while ( sideNode != theSideSecondNode );
8348
8349     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8350       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8351       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8352     }
8353   } // end nodes search on the side 2
8354
8355   // ============================
8356   // sew the border to the side 2
8357   // ============================
8358
8359   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8360   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8361
8362   TListOfListOfNodes nodeGroupsToMerge;
8363   if ( nbNodes[0] == nbNodes[1] ||
8364        ( theSideIsFreeBorder && !theSideThirdNode)) {
8365
8366     // all nodes are to be merged
8367
8368     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8369          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8370          nIt[0]++, nIt[1]++ )
8371     {
8372       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8373       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8374       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8375     }
8376   }
8377   else {
8378
8379     // insert new nodes into the border and the side to get equal nb of segments
8380
8381     // get normalized parameters of nodes on the borders
8382     //double param[ 2 ][ maxNbNodes ];
8383     double* param[ 2 ];
8384     param[0] = new double [ maxNbNodes ];
8385     param[1] = new double [ maxNbNodes ];
8386     int iNode, iBord;
8387     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8388       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8389       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8390       const SMDS_MeshNode* nPrev = *nIt;
8391       double bordLength = 0;
8392       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8393         const SMDS_MeshNode* nCur = *nIt;
8394         gp_XYZ segment (nCur->X() - nPrev->X(),
8395                         nCur->Y() - nPrev->Y(),
8396                         nCur->Z() - nPrev->Z());
8397         double segmentLen = segment.Modulus();
8398         bordLength += segmentLen;
8399         param[ iBord ][ iNode ] = bordLength;
8400         nPrev = nCur;
8401       }
8402       // normalize within [0,1]
8403       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8404         param[ iBord ][ iNode ] /= bordLength;
8405       }
8406     }
8407
8408     // loop on border segments
8409     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8410     int i[ 2 ] = { 0, 0 };
8411     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8412     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8413
8414     TElemOfNodeListMap insertMap;
8415     TElemOfNodeListMap::iterator insertMapIt;
8416     // insertMap is
8417     // key:   elem to insert nodes into
8418     // value: 2 nodes to insert between + nodes to be inserted
8419     do {
8420       bool next[ 2 ] = { false, false };
8421
8422       // find min adjacent segment length after sewing
8423       double nextParam = 10., prevParam = 0;
8424       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8425         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8426           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8427         if ( i[ iBord ] > 0 )
8428           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8429       }
8430       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8431       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8432       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8433
8434       // choose to insert or to merge nodes
8435       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8436       if ( Abs( du ) <= minSegLen * 0.2 ) {
8437         // merge
8438         // ------
8439         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8440         const SMDS_MeshNode* n0 = *nIt[0];
8441         const SMDS_MeshNode* n1 = *nIt[1];
8442         nodeGroupsToMerge.back().push_back( n1 );
8443         nodeGroupsToMerge.back().push_back( n0 );
8444         // position of node of the border changes due to merge
8445         param[ 0 ][ i[0] ] += du;
8446         // move n1 for the sake of elem shape evaluation during insertion.
8447         // n1 will be removed by MergeNodes() anyway
8448         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8449         next[0] = next[1] = true;
8450       }
8451       else {
8452         // insert
8453         // ------
8454         int intoBord = ( du < 0 ) ? 0 : 1;
8455         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8456         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8457         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8458         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8459         if ( intoBord == 1 ) {
8460           // move node of the border to be on a link of elem of the side
8461           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8462           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8463           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8464           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8465           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8466         }
8467         insertMapIt = insertMap.find( elem );
8468         bool notFound = ( insertMapIt == insertMap.end() );
8469         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8470         if ( otherLink ) {
8471           // insert into another link of the same element:
8472           // 1. perform insertion into the other link of the elem
8473           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8474           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8475           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8476           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8477           // 2. perform insertion into the link of adjacent faces
8478           while (true) {
8479             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8480             if ( adjElem )
8481               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8482             else
8483               break;
8484           }
8485           if (toCreatePolyedrs) {
8486             // perform insertion into the links of adjacent volumes
8487             UpdateVolumes(n12, n22, nodeList);
8488           }
8489           // 3. find an element appeared on n1 and n2 after the insertion
8490           insertMap.erase( elem );
8491           elem = findAdjacentFace( n1, n2, 0 );
8492         }
8493         if ( notFound || otherLink ) {
8494           // add element and nodes of the side into the insertMap
8495           insertMapIt = insertMap.insert
8496             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8497           (*insertMapIt).second.push_back( n1 );
8498           (*insertMapIt).second.push_back( n2 );
8499         }
8500         // add node to be inserted into elem
8501         (*insertMapIt).second.push_back( nIns );
8502         next[ 1 - intoBord ] = true;
8503       }
8504
8505       // go to the next segment
8506       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8507         if ( next[ iBord ] ) {
8508           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8509             eIt[ iBord ]++;
8510           nPrev[ iBord ] = *nIt[ iBord ];
8511           nIt[ iBord ]++; i[ iBord ]++;
8512         }
8513       }
8514     }
8515     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8516
8517     // perform insertion of nodes into elements
8518
8519     for (insertMapIt = insertMap.begin();
8520          insertMapIt != insertMap.end();
8521          insertMapIt++ )
8522     {
8523       const SMDS_MeshElement* elem = (*insertMapIt).first;
8524       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8525       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8526       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8527
8528       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8529
8530       if ( !theSideIsFreeBorder ) {
8531         // look for and insert nodes into the faces adjacent to elem
8532         while (true) {
8533           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8534           if ( adjElem )
8535             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8536           else
8537             break;
8538         }
8539       }
8540       if (toCreatePolyedrs) {
8541         // perform insertion into the links of adjacent volumes
8542         UpdateVolumes(n1, n2, nodeList);
8543       }
8544     }
8545
8546     delete param[0];
8547     delete param[1];
8548   } // end: insert new nodes
8549
8550   MergeNodes ( nodeGroupsToMerge );
8551
8552   return aResult;
8553 }
8554
8555 //=======================================================================
8556 //function : InsertNodesIntoLink
8557 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8558 //           and theBetweenNode2 and split theElement
8559 //=======================================================================
8560
8561 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8562                                            const SMDS_MeshNode*        theBetweenNode1,
8563                                            const SMDS_MeshNode*        theBetweenNode2,
8564                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8565                                            const bool                  toCreatePoly)
8566 {
8567   if ( theFace->GetType() != SMDSAbs_Face ) return;
8568
8569   // find indices of 2 link nodes and of the rest nodes
8570   int iNode = 0, il1, il2, i3, i4;
8571   il1 = il2 = i3 = i4 = -1;
8572   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8573   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8574
8575   if(theFace->IsQuadratic()) {
8576     const SMDS_VtkFace* F =
8577       dynamic_cast<const SMDS_VtkFace*>(theFace);
8578     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8579     // use special nodes iterator
8580     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8581     while( anIter->more() ) {
8582       const SMDS_MeshNode* n = cast2Node(anIter->next());
8583       if ( n == theBetweenNode1 )
8584         il1 = iNode;
8585       else if ( n == theBetweenNode2 )
8586         il2 = iNode;
8587       else if ( i3 < 0 )
8588         i3 = iNode;
8589       else
8590         i4 = iNode;
8591       nodes[ iNode++ ] = n;
8592     }
8593   }
8594   else {
8595     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8596     while ( nodeIt->more() ) {
8597       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8598       if ( n == theBetweenNode1 )
8599         il1 = iNode;
8600       else if ( n == theBetweenNode2 )
8601         il2 = iNode;
8602       else if ( i3 < 0 )
8603         i3 = iNode;
8604       else
8605         i4 = iNode;
8606       nodes[ iNode++ ] = n;
8607     }
8608   }
8609   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8610     return ;
8611
8612   // arrange link nodes to go one after another regarding the face orientation
8613   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8614   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8615   if ( reverse ) {
8616     iNode = il1;
8617     il1 = il2;
8618     il2 = iNode;
8619     aNodesToInsert.reverse();
8620   }
8621   // check that not link nodes of a quadrangles are in good order
8622   int nbFaceNodes = theFace->NbNodes();
8623   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8624     iNode = i3;
8625     i3 = i4;
8626     i4 = iNode;
8627   }
8628
8629   if (toCreatePoly || theFace->IsPoly()) {
8630
8631     iNode = 0;
8632     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8633
8634     // add nodes of face up to first node of link
8635     bool isFLN = false;
8636
8637     if(theFace->IsQuadratic()) {
8638       const SMDS_VtkFace* F =
8639         dynamic_cast<const SMDS_VtkFace*>(theFace);
8640       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8641       // use special nodes iterator
8642       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8643       while( anIter->more()  && !isFLN ) {
8644         const SMDS_MeshNode* n = cast2Node(anIter->next());
8645         poly_nodes[iNode++] = n;
8646         if (n == nodes[il1]) {
8647           isFLN = true;
8648         }
8649       }
8650       // add nodes to insert
8651       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8652       for (; nIt != aNodesToInsert.end(); nIt++) {
8653         poly_nodes[iNode++] = *nIt;
8654       }
8655       // add nodes of face starting from last node of link
8656       while ( anIter->more() ) {
8657         poly_nodes[iNode++] = cast2Node(anIter->next());
8658       }
8659     }
8660     else {
8661       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8662       while ( nodeIt->more() && !isFLN ) {
8663         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8664         poly_nodes[iNode++] = n;
8665         if (n == nodes[il1]) {
8666           isFLN = true;
8667         }
8668       }
8669       // add nodes to insert
8670       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8671       for (; nIt != aNodesToInsert.end(); nIt++) {
8672         poly_nodes[iNode++] = *nIt;
8673       }
8674       // add nodes of face starting from last node of link
8675       while ( nodeIt->more() ) {
8676         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8677         poly_nodes[iNode++] = n;
8678       }
8679     }
8680
8681     // edit or replace the face
8682     SMESHDS_Mesh *aMesh = GetMeshDS();
8683
8684     if (theFace->IsPoly()) {
8685       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8686     }
8687     else {
8688       int aShapeId = FindShape( theFace );
8689
8690       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8691       myLastCreatedElems.Append(newElem);
8692       if ( aShapeId && newElem )
8693         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8694
8695       aMesh->RemoveElement(theFace);
8696     }
8697     return;
8698   }
8699
8700   SMESHDS_Mesh *aMesh = GetMeshDS();
8701   if( !theFace->IsQuadratic() ) {
8702
8703     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8704     int nbLinkNodes = 2 + aNodesToInsert.size();
8705     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8706     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8707     linkNodes[ 0 ] = nodes[ il1 ];
8708     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8709     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8711       linkNodes[ iNode++ ] = *nIt;
8712     }
8713     // decide how to split a quadrangle: compare possible variants
8714     // and choose which of splits to be a quadrangle
8715     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8716     if ( nbFaceNodes == 3 ) {
8717       iBestQuad = nbSplits;
8718       i4 = i3;
8719     }
8720     else if ( nbFaceNodes == 4 ) {
8721       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8722       double aBestRate = DBL_MAX;
8723       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8724         i1 = 0; i2 = 1;
8725         double aBadRate = 0;
8726         // evaluate elements quality
8727         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8728           if ( iSplit == iQuad ) {
8729             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8730                                    linkNodes[ i2++ ],
8731                                    nodes[ i3 ],
8732                                    nodes[ i4 ]);
8733             aBadRate += getBadRate( &quad, aCrit );
8734           }
8735           else {
8736             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8737                                    linkNodes[ i2++ ],
8738                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8739             aBadRate += getBadRate( &tria, aCrit );
8740           }
8741         }
8742         // choice
8743         if ( aBadRate < aBestRate ) {
8744           iBestQuad = iQuad;
8745           aBestRate = aBadRate;
8746         }
8747       }
8748     }
8749
8750     // create new elements
8751     int aShapeId = FindShape( theFace );
8752
8753     i1 = 0; i2 = 1;
8754     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8755       SMDS_MeshElement* newElem = 0;
8756       if ( iSplit == iBestQuad )
8757         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8758                                   linkNodes[ i2++ ],
8759                                   nodes[ i3 ],
8760                                   nodes[ i4 ]);
8761       else
8762         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8763                                   linkNodes[ i2++ ],
8764                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8765       myLastCreatedElems.Append(newElem);
8766       if ( aShapeId && newElem )
8767         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8768     }
8769
8770     // change nodes of theFace
8771     const SMDS_MeshNode* newNodes[ 4 ];
8772     newNodes[ 0 ] = linkNodes[ i1 ];
8773     newNodes[ 1 ] = linkNodes[ i2 ];
8774     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8775     newNodes[ 3 ] = nodes[ i4 ];
8776     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8777     const SMDS_MeshElement* newElem = 0;
8778     if (iSplit == iBestQuad)
8779       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8780     else
8781       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8782     myLastCreatedElems.Append(newElem);
8783     if ( aShapeId && newElem )
8784       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8785 } // end if(!theFace->IsQuadratic())
8786   else { // theFace is quadratic
8787     // we have to split theFace on simple triangles and one simple quadrangle
8788     int tmp = il1/2;
8789     int nbshift = tmp*2;
8790     // shift nodes in nodes[] by nbshift
8791     int i,j;
8792     for(i=0; i<nbshift; i++) {
8793       const SMDS_MeshNode* n = nodes[0];
8794       for(j=0; j<nbFaceNodes-1; j++) {
8795         nodes[j] = nodes[j+1];
8796       }
8797       nodes[nbFaceNodes-1] = n;
8798     }
8799     il1 = il1 - nbshift;
8800     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8801     //   n0      n1     n2    n0      n1     n2
8802     //     +-----+-----+        +-----+-----+
8803     //      \         /         |           |
8804     //       \       /          |           |
8805     //      n5+     +n3       n7+           +n3
8806     //         \   /            |           |
8807     //          \ /             |           |
8808     //           +              +-----+-----+
8809     //           n4           n6      n5     n4
8810
8811     // create new elements
8812     int aShapeId = FindShape( theFace );
8813
8814     int n1,n2,n3;
8815     if(nbFaceNodes==6) { // quadratic triangle
8816       SMDS_MeshElement* newElem =
8817         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8818       myLastCreatedElems.Append(newElem);
8819       if ( aShapeId && newElem )
8820         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8821       if(theFace->IsMediumNode(nodes[il1])) {
8822         // create quadrangle
8823         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8824         myLastCreatedElems.Append(newElem);
8825         if ( aShapeId && newElem )
8826           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8827         n1 = 1;
8828         n2 = 2;
8829         n3 = 3;
8830       }
8831       else {
8832         // create quadrangle
8833         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8834         myLastCreatedElems.Append(newElem);
8835         if ( aShapeId && newElem )
8836           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8837         n1 = 0;
8838         n2 = 1;
8839         n3 = 5;
8840       }
8841     }
8842     else { // nbFaceNodes==8 - quadratic quadrangle
8843       SMDS_MeshElement* newElem =
8844         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8845       myLastCreatedElems.Append(newElem);
8846       if ( aShapeId && newElem )
8847         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8848       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8849       myLastCreatedElems.Append(newElem);
8850       if ( aShapeId && newElem )
8851         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8852       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8853       myLastCreatedElems.Append(newElem);
8854       if ( aShapeId && newElem )
8855         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8856       if(theFace->IsMediumNode(nodes[il1])) {
8857         // create quadrangle
8858         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8859         myLastCreatedElems.Append(newElem);
8860         if ( aShapeId && newElem )
8861           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8862         n1 = 1;
8863         n2 = 2;
8864         n3 = 3;
8865       }
8866       else {
8867         // create quadrangle
8868         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8869         myLastCreatedElems.Append(newElem);
8870         if ( aShapeId && newElem )
8871           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8872         n1 = 0;
8873         n2 = 1;
8874         n3 = 7;
8875       }
8876     }
8877     // create needed triangles using n1,n2,n3 and inserted nodes
8878     int nbn = 2 + aNodesToInsert.size();
8879     //const SMDS_MeshNode* aNodes[nbn];
8880     vector<const SMDS_MeshNode*> aNodes(nbn);
8881     aNodes[0] = nodes[n1];
8882     aNodes[nbn-1] = nodes[n2];
8883     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8884     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8885       aNodes[iNode++] = *nIt;
8886     }
8887     for(i=1; i<nbn; i++) {
8888       SMDS_MeshElement* newElem =
8889         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8890       myLastCreatedElems.Append(newElem);
8891       if ( aShapeId && newElem )
8892         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8893     }
8894   }
8895   // remove old face
8896   aMesh->RemoveElement(theFace);
8897 }
8898
8899 //=======================================================================
8900 //function : UpdateVolumes
8901 //purpose  :
8902 //=======================================================================
8903 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8904                                       const SMDS_MeshNode*        theBetweenNode2,
8905                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8906 {
8907   myLastCreatedElems.Clear();
8908   myLastCreatedNodes.Clear();
8909
8910   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8911   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8912     const SMDS_MeshElement* elem = invElemIt->next();
8913
8914     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8915     SMDS_VolumeTool aVolume (elem);
8916     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8917       continue;
8918
8919     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8920     int iface, nbFaces = aVolume.NbFaces();
8921     vector<const SMDS_MeshNode *> poly_nodes;
8922     vector<int> quantities (nbFaces);
8923
8924     for (iface = 0; iface < nbFaces; iface++) {
8925       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8926       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8927       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8928
8929       for (int inode = 0; inode < nbFaceNodes; inode++) {
8930         poly_nodes.push_back(faceNodes[inode]);
8931
8932         if (nbInserted == 0) {
8933           if (faceNodes[inode] == theBetweenNode1) {
8934             if (faceNodes[inode + 1] == theBetweenNode2) {
8935               nbInserted = theNodesToInsert.size();
8936
8937               // add nodes to insert
8938               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8939               for (; nIt != theNodesToInsert.end(); nIt++) {
8940                 poly_nodes.push_back(*nIt);
8941               }
8942             }
8943           }
8944           else if (faceNodes[inode] == theBetweenNode2) {
8945             if (faceNodes[inode + 1] == theBetweenNode1) {
8946               nbInserted = theNodesToInsert.size();
8947
8948               // add nodes to insert in reversed order
8949               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8950               nIt--;
8951               for (; nIt != theNodesToInsert.begin(); nIt--) {
8952                 poly_nodes.push_back(*nIt);
8953               }
8954               poly_nodes.push_back(*nIt);
8955             }
8956           }
8957           else {
8958           }
8959         }
8960       }
8961       quantities[iface] = nbFaceNodes + nbInserted;
8962     }
8963
8964     // Replace or update the volume
8965     SMESHDS_Mesh *aMesh = GetMeshDS();
8966
8967     if (elem->IsPoly()) {
8968       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8969
8970     }
8971     else {
8972       int aShapeId = FindShape( elem );
8973
8974       SMDS_MeshElement* newElem =
8975         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8976       myLastCreatedElems.Append(newElem);
8977       if (aShapeId && newElem)
8978         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8979
8980       aMesh->RemoveElement(elem);
8981     }
8982   }
8983 }
8984
8985 namespace
8986 {
8987   //================================================================================
8988   /*!
8989    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8990    */
8991   //================================================================================
8992
8993   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8994                            vector<const SMDS_MeshNode *> & nodes,
8995                            vector<int> &                   nbNodeInFaces )
8996   {
8997     nodes.clear();
8998     nbNodeInFaces.clear();
8999     SMDS_VolumeTool vTool ( elem );
9000     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9001     {
9002       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9003       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9004       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9005     }
9006   }
9007 }
9008
9009 //=======================================================================
9010 /*!
9011  * \brief Convert elements contained in a submesh to quadratic
9012  * \return int - nb of checked elements
9013  */
9014 //=======================================================================
9015
9016 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9017                                              SMESH_MesherHelper& theHelper,
9018                                              const bool          theForce3d)
9019 {
9020   int nbElem = 0;
9021   if( !theSm ) return nbElem;
9022
9023   vector<int> nbNodeInFaces;
9024   vector<const SMDS_MeshNode *> nodes;
9025   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9026   while(ElemItr->more())
9027   {
9028     nbElem++;
9029     const SMDS_MeshElement* elem = ElemItr->next();
9030     if( !elem ) continue;
9031
9032     // analyse a necessity of conversion
9033     const SMDSAbs_ElementType aType = elem->GetType();
9034     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9035       continue;
9036     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9037     bool hasCentralNodes = false;
9038     if ( elem->IsQuadratic() )
9039     {
9040       bool alreadyOK;
9041       switch ( aGeomType ) {
9042       case SMDSEntity_Quad_Triangle:
9043       case SMDSEntity_Quad_Quadrangle:
9044       case SMDSEntity_Quad_Hexa:
9045         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9046
9047       case SMDSEntity_BiQuad_Triangle:
9048       case SMDSEntity_BiQuad_Quadrangle:
9049       case SMDSEntity_TriQuad_Hexa:
9050         alreadyOK = theHelper.GetIsBiQuadratic();
9051         hasCentralNodes = true;
9052         break;
9053       default:
9054         alreadyOK = true;
9055       }
9056       // take into account already present modium nodes
9057       switch ( aType ) {
9058       case SMDSAbs_Volume:
9059         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9060       case SMDSAbs_Face:
9061         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9062       case SMDSAbs_Edge:
9063         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9064       default:;
9065       }
9066       if ( alreadyOK )
9067         continue;
9068     }
9069     // get elem data needed to re-create it
9070     //
9071     const int id      = elem->GetID();
9072     const int nbNodes = elem->NbCornerNodes();
9073     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9074     if ( aGeomType == SMDSEntity_Polyhedra )
9075       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9076     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9077       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9078
9079     // remove a linear element
9080     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9081
9082     // remove central nodes of biquadratic elements (biquad->quad convertion)
9083     if ( hasCentralNodes )
9084       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9085         if ( nodes[i]->NbInverseElements() == 0 )
9086           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9087
9088     const SMDS_MeshElement* NewElem = 0;
9089
9090     switch( aType )
9091     {
9092     case SMDSAbs_Edge :
9093       {
9094         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9095         break;
9096       }
9097     case SMDSAbs_Face :
9098       {
9099         switch(nbNodes)
9100         {
9101         case 3:
9102           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9103           break;
9104         case 4:
9105           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9106           break;
9107         default:
9108           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9109         }
9110         break;
9111       }
9112     case SMDSAbs_Volume :
9113       {
9114         switch( aGeomType )
9115         {
9116         case SMDSEntity_Tetra:
9117           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9118           break;
9119         case SMDSEntity_Pyramid:
9120           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9121           break;
9122         case SMDSEntity_Penta:
9123           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9124           break;
9125         case SMDSEntity_Hexa:
9126         case SMDSEntity_Quad_Hexa:
9127         case SMDSEntity_TriQuad_Hexa:
9128           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9129                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9130           break;
9131         case SMDSEntity_Hexagonal_Prism:
9132         default:
9133           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9134         }
9135         break;
9136       }
9137     default :
9138       continue;
9139     }
9140     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9141     if( NewElem && NewElem->getshapeId() < 1 )
9142       theSm->AddElement( NewElem );
9143   }
9144   return nbElem;
9145 }
9146 //=======================================================================
9147 //function : ConvertToQuadratic
9148 //purpose  :
9149 //=======================================================================
9150
9151 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9152 {
9153   SMESHDS_Mesh* meshDS = GetMeshDS();
9154
9155   SMESH_MesherHelper aHelper(*myMesh);
9156
9157   aHelper.SetIsQuadratic( true );
9158   aHelper.SetIsBiQuadratic( theToBiQuad );
9159   aHelper.SetElementsOnShape(true);
9160   aHelper.ToFixNodeParameters( true );
9161
9162   // convert elements assigned to sub-meshes
9163   int nbCheckedElems = 0;
9164   if ( myMesh->HasShapeToMesh() )
9165   {
9166     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9167     {
9168       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9169       while ( smIt->more() ) {
9170         SMESH_subMesh* sm = smIt->next();
9171         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9172           aHelper.SetSubShape( sm->GetSubShape() );
9173           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9174         }
9175       }
9176     }
9177   }
9178
9179   // convert elements NOT assigned to sub-meshes
9180   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9181   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9182   {
9183     aHelper.SetElementsOnShape(false);
9184     SMESHDS_SubMesh *smDS = 0;
9185
9186     // convert edges
9187     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9188     while( aEdgeItr->more() )
9189     {
9190       const SMDS_MeshEdge* edge = aEdgeItr->next();
9191       if ( !edge->IsQuadratic() )
9192       {
9193         int                  id = edge->GetID();
9194         const SMDS_MeshNode* n1 = edge->GetNode(0);
9195         const SMDS_MeshNode* n2 = edge->GetNode(1);
9196
9197         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9198
9199         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9200         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9201       }
9202       else
9203       {
9204         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9205       }
9206     }
9207
9208     // convert faces
9209     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9210     while( aFaceItr->more() )
9211     {
9212       const SMDS_MeshFace* face = aFaceItr->next();
9213       if ( !face ) continue;
9214       
9215       const SMDSAbs_EntityType type = face->GetEntityType();
9216       bool alreadyOK;
9217       switch( type )
9218       {
9219       case SMDSEntity_Quad_Triangle:
9220       case SMDSEntity_Quad_Quadrangle:
9221         alreadyOK = !theToBiQuad;
9222         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9223         break;
9224       case SMDSEntity_BiQuad_Triangle:
9225       case SMDSEntity_BiQuad_Quadrangle:
9226         alreadyOK = theToBiQuad;
9227         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9228         break;
9229       default: alreadyOK = false;
9230       }
9231       if ( alreadyOK )
9232         continue;
9233
9234       const int id = face->GetID();
9235       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9236
9237       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9238
9239       SMDS_MeshFace * NewFace = 0;
9240       switch( type )
9241       {
9242       case SMDSEntity_Triangle:
9243       case SMDSEntity_Quad_Triangle:
9244       case SMDSEntity_BiQuad_Triangle:
9245         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9246         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9247           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9248         break;
9249
9250       case SMDSEntity_Quadrangle:
9251       case SMDSEntity_Quad_Quadrangle:
9252       case SMDSEntity_BiQuad_Quadrangle:
9253         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9254         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9255           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9256         break;
9257
9258       default:;
9259         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9260       }
9261       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9262     }
9263
9264     // convert volumes
9265     vector<int> nbNodeInFaces;
9266     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9267     while(aVolumeItr->more())
9268     {
9269       const SMDS_MeshVolume* volume = aVolumeItr->next();
9270       if ( !volume ) continue;
9271
9272       const SMDSAbs_EntityType type = volume->GetEntityType();
9273       if ( volume->IsQuadratic() )
9274       {
9275         bool alreadyOK;
9276         switch ( type )
9277         {
9278         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9279         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9280         default:                      alreadyOK = true;
9281         }
9282         if ( alreadyOK )
9283         {
9284           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9285           continue;
9286         }
9287       }
9288       const int id = volume->GetID();
9289       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9290       if ( type == SMDSEntity_Polyhedra )
9291         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9292       else if ( type == SMDSEntity_Hexagonal_Prism )
9293         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9294
9295       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9296
9297       SMDS_MeshVolume * NewVolume = 0;
9298       switch ( type )
9299       {
9300       case SMDSEntity_Tetra:
9301         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9302         break;
9303       case SMDSEntity_Hexa:
9304       case SMDSEntity_Quad_Hexa:
9305       case SMDSEntity_TriQuad_Hexa:
9306         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9307                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9308         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9309           if ( nodes[i]->NbInverseElements() == 0 )
9310             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9311         break;
9312       case SMDSEntity_Pyramid:
9313         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9314                                       nodes[3], nodes[4], id, theForce3d);
9315         break;
9316       case SMDSEntity_Penta:
9317         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9318                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9319         break;
9320       case SMDSEntity_Hexagonal_Prism:
9321       default:
9322         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9323       }
9324       ReplaceElemInGroups(volume, NewVolume, meshDS);
9325     }
9326   }
9327
9328   if ( !theForce3d )
9329   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9330     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9331     // aHelper.FixQuadraticElements(myError);
9332     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9333   }
9334 }
9335
9336 //================================================================================
9337 /*!
9338  * \brief Makes given elements quadratic
9339  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9340  *  \param theElements - elements to make quadratic
9341  */
9342 //================================================================================
9343
9344 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9345                                           TIDSortedElemSet& theElements,
9346                                           const bool        theToBiQuad)
9347 {
9348   if ( theElements.empty() ) return;
9349
9350   // we believe that all theElements are of the same type
9351   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9352
9353   // get all nodes shared by theElements
9354   TIDSortedNodeSet allNodes;
9355   TIDSortedElemSet::iterator eIt = theElements.begin();
9356   for ( ; eIt != theElements.end(); ++eIt )
9357     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9358
9359   // complete theElements with elements of lower dim whose all nodes are in allNodes
9360
9361   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9362   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9363   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9364   for ( ; nIt != allNodes.end(); ++nIt )
9365   {
9366     const SMDS_MeshNode* n = *nIt;
9367     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9368     while ( invIt->more() )
9369     {
9370       const SMDS_MeshElement*      e = invIt->next();
9371       const SMDSAbs_ElementType type = e->GetType();
9372       if ( e->IsQuadratic() )
9373       {
9374         quadAdjacentElems[ type ].insert( e );
9375
9376         bool alreadyOK;
9377         switch ( e->GetEntityType() ) {
9378         case SMDSEntity_Quad_Triangle:
9379         case SMDSEntity_Quad_Quadrangle:
9380         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9381         case SMDSEntity_BiQuad_Triangle:
9382         case SMDSEntity_BiQuad_Quadrangle:
9383         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9384         default:                           alreadyOK = true;
9385         }
9386         if ( alreadyOK )
9387           continue;
9388       }
9389       if ( type >= elemType )
9390         continue; // same type or more complex linear element
9391
9392       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9393         continue; // e is already checked
9394
9395       // check nodes
9396       bool allIn = true;
9397       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9398       while ( nodeIt->more() && allIn )
9399         allIn = allNodes.count( nodeIt->next() );
9400       if ( allIn )
9401         theElements.insert(e );
9402     }
9403   }
9404
9405   SMESH_MesherHelper helper(*myMesh);
9406   helper.SetIsQuadratic( true );
9407   helper.SetIsBiQuadratic( theToBiQuad );
9408
9409   // add links of quadratic adjacent elements to the helper
9410
9411   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9412     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9413           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9414     {
9415       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9416     }
9417   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9418     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9419           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9420     {
9421       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9422     }
9423   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9424     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9425           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9426     {
9427       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9428     }
9429
9430   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9431
9432   SMESHDS_Mesh*  meshDS = GetMeshDS();
9433   SMESHDS_SubMesh* smDS = 0;
9434   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9435   {
9436     const SMDS_MeshElement* elem = *eIt;
9437
9438     bool alreadyOK;
9439     int nbCentralNodes = 0;
9440     switch ( elem->GetEntityType() ) {
9441       // linear convertible
9442     case SMDSEntity_Edge:
9443     case SMDSEntity_Triangle:
9444     case SMDSEntity_Quadrangle:
9445     case SMDSEntity_Tetra:
9446     case SMDSEntity_Pyramid:
9447     case SMDSEntity_Hexa:
9448     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9449       // quadratic that can become bi-quadratic
9450     case SMDSEntity_Quad_Triangle:
9451     case SMDSEntity_Quad_Quadrangle:
9452     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9453       // bi-quadratic
9454     case SMDSEntity_BiQuad_Triangle:
9455     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9456     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9457       // the rest
9458     default:                           alreadyOK = true;
9459     }
9460     if ( alreadyOK ) continue;
9461
9462     const SMDSAbs_ElementType type = elem->GetType();
9463     const int                   id = elem->GetID();
9464     const int              nbNodes = elem->NbCornerNodes();
9465     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9466
9467     helper.SetSubShape( elem->getshapeId() );
9468
9469     if ( !smDS || !smDS->Contains( elem ))
9470       smDS = meshDS->MeshElements( elem->getshapeId() );
9471     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9472
9473     SMDS_MeshElement * newElem = 0;
9474     switch( nbNodes )
9475     {
9476     case 4: // cases for most frequently used element types go first (for optimization)
9477       if ( type == SMDSAbs_Volume )
9478         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9479       else
9480         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9481       break;
9482     case 8:
9483       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9484                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9485       break;
9486     case 3:
9487       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9488       break;
9489     case 2:
9490       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9491       break;
9492     case 5:
9493       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9494                                  nodes[4], id, theForce3d);
9495       break;
9496     case 6:
9497       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9498                                  nodes[4], nodes[5], id, theForce3d);
9499       break;
9500     default:;
9501     }
9502     ReplaceElemInGroups( elem, newElem, meshDS);
9503     if( newElem && smDS )
9504       smDS->AddElement( newElem );
9505
9506      // remove central nodes
9507     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9508       if ( nodes[i]->NbInverseElements() == 0 )
9509         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9510
9511   } // loop on theElements
9512
9513   if ( !theForce3d )
9514   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9515     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9516     // helper.FixQuadraticElements( myError );
9517     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9518   }
9519 }
9520
9521 //=======================================================================
9522 /*!
9523  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9524  * \return int - nb of checked elements
9525  */
9526 //=======================================================================
9527
9528 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9529                                      SMDS_ElemIteratorPtr theItr,
9530                                      const int            theShapeID)
9531 {
9532   int nbElem = 0;
9533   SMESHDS_Mesh* meshDS = GetMeshDS();
9534
9535   while( theItr->more() )
9536   {
9537     const SMDS_MeshElement* elem = theItr->next();
9538     nbElem++;
9539     if( elem && elem->IsQuadratic())
9540     {
9541       int id                    = elem->GetID();
9542       int nbCornerNodes         = elem->NbCornerNodes();
9543       SMDSAbs_ElementType aType = elem->GetType();
9544
9545       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9546
9547       //remove a quadratic element
9548       if ( !theSm || !theSm->Contains( elem ))
9549         theSm = meshDS->MeshElements( elem->getshapeId() );
9550       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9551
9552       // remove medium nodes
9553       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9554         if ( nodes[i]->NbInverseElements() == 0 )
9555           meshDS->RemoveFreeNode( nodes[i], theSm );
9556
9557       // add a linear element
9558       nodes.resize( nbCornerNodes );
9559       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9560       ReplaceElemInGroups(elem, newElem, meshDS);
9561       if( theSm && newElem )
9562         theSm->AddElement( newElem );
9563     }
9564   }
9565   return nbElem;
9566 }
9567
9568 //=======================================================================
9569 //function : ConvertFromQuadratic
9570 //purpose  :
9571 //=======================================================================
9572
9573 bool SMESH_MeshEditor::ConvertFromQuadratic()
9574 {
9575   int nbCheckedElems = 0;
9576   if ( myMesh->HasShapeToMesh() )
9577   {
9578     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9579     {
9580       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9581       while ( smIt->more() ) {
9582         SMESH_subMesh* sm = smIt->next();
9583         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9584           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9585       }
9586     }
9587   }
9588
9589   int totalNbElems =
9590     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9591   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9592   {
9593     SMESHDS_SubMesh *aSM = 0;
9594     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9595   }
9596
9597   return true;
9598 }
9599
9600 namespace
9601 {
9602   //================================================================================
9603   /*!
9604    * \brief Return true if all medium nodes of the element are in the node set
9605    */
9606   //================================================================================
9607
9608   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9609   {
9610     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9611       if ( !nodeSet.count( elem->GetNode(i) ))
9612         return false;
9613     return true;
9614   }
9615 }
9616
9617 //================================================================================
9618 /*!
9619  * \brief Makes given elements linear
9620  */
9621 //================================================================================
9622
9623 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9624 {
9625   if ( theElements.empty() ) return;
9626
9627   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9628   set<int> mediumNodeIDs;
9629   TIDSortedElemSet::iterator eIt = theElements.begin();
9630   for ( ; eIt != theElements.end(); ++eIt )
9631   {
9632     const SMDS_MeshElement* e = *eIt;
9633     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9634       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9635   }
9636
9637   // replace given elements by linear ones
9638   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9639   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9640
9641   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9642   // except those elements sharing medium nodes of quadratic element whose medium nodes
9643   // are not all in mediumNodeIDs
9644
9645   // get remaining medium nodes
9646   TIDSortedNodeSet mediumNodes;
9647   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9648   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9649     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9650       mediumNodes.insert( mediumNodes.end(), n );
9651
9652   // find more quadratic elements to convert
9653   TIDSortedElemSet moreElemsToConvert;
9654   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9655   for ( ; nIt != mediumNodes.end(); ++nIt )
9656   {
9657     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9658     while ( invIt->more() )
9659     {
9660       const SMDS_MeshElement* e = invIt->next();
9661       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9662       {
9663         // find a more complex element including e and
9664         // whose medium nodes are not in mediumNodes
9665         bool complexFound = false;
9666         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9667         {
9668           SMDS_ElemIteratorPtr invIt2 =
9669             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9670           while ( invIt2->more() )
9671           {
9672             const SMDS_MeshElement* eComplex = invIt2->next();
9673             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9674             {
9675               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9676               if ( nbCommonNodes == e->NbNodes())
9677               {
9678                 complexFound = true;
9679                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9680                 break;
9681               }
9682             }
9683           }
9684         }
9685         if ( !complexFound )
9686           moreElemsToConvert.insert( e );
9687       }
9688     }
9689   }
9690   elemIt = elemSetIterator( moreElemsToConvert );
9691   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9692 }
9693
9694 //=======================================================================
9695 //function : SewSideElements
9696 //purpose  :
9697 //=======================================================================
9698
9699 SMESH_MeshEditor::Sew_Error
9700 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9701                                    TIDSortedElemSet&    theSide2,
9702                                    const SMDS_MeshNode* theFirstNode1,
9703                                    const SMDS_MeshNode* theFirstNode2,
9704                                    const SMDS_MeshNode* theSecondNode1,
9705                                    const SMDS_MeshNode* theSecondNode2)
9706 {
9707   myLastCreatedElems.Clear();
9708   myLastCreatedNodes.Clear();
9709
9710   MESSAGE ("::::SewSideElements()");
9711   if ( theSide1.size() != theSide2.size() )
9712     return SEW_DIFF_NB_OF_ELEMENTS;
9713
9714   Sew_Error aResult = SEW_OK;
9715   // Algo:
9716   // 1. Build set of faces representing each side
9717   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9718   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9719
9720   // =======================================================================
9721   // 1. Build set of faces representing each side:
9722   // =======================================================================
9723   // a. build set of nodes belonging to faces
9724   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9725   // c. create temporary faces representing side of volumes if correspondent
9726   //    face does not exist
9727
9728   SMESHDS_Mesh* aMesh = GetMeshDS();
9729   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9730   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9731   TIDSortedElemSet             faceSet1, faceSet2;
9732   set<const SMDS_MeshElement*> volSet1,  volSet2;
9733   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9734   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9735   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9736   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9737   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9738   int iSide, iFace, iNode;
9739
9740   list<const SMDS_MeshElement* > tempFaceList;
9741   for ( iSide = 0; iSide < 2; iSide++ ) {
9742     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9743     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9744     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9745     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9746     set<const SMDS_MeshElement*>::iterator vIt;
9747     TIDSortedElemSet::iterator eIt;
9748     set<const SMDS_MeshNode*>::iterator    nIt;
9749
9750     // check that given nodes belong to given elements
9751     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9752     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9753     int firstIndex = -1, secondIndex = -1;
9754     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9755       const SMDS_MeshElement* elem = *eIt;
9756       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9757       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9758       if ( firstIndex > -1 && secondIndex > -1 ) break;
9759     }
9760     if ( firstIndex < 0 || secondIndex < 0 ) {
9761       // we can simply return until temporary faces created
9762       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9763     }
9764
9765     // -----------------------------------------------------------
9766     // 1a. Collect nodes of existing faces
9767     //     and build set of face nodes in order to detect missing
9768     //     faces corresponding to sides of volumes
9769     // -----------------------------------------------------------
9770
9771     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9772
9773     // loop on the given element of a side
9774     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9775       //const SMDS_MeshElement* elem = *eIt;
9776       const SMDS_MeshElement* elem = *eIt;
9777       if ( elem->GetType() == SMDSAbs_Face ) {
9778         faceSet->insert( elem );
9779         set <const SMDS_MeshNode*> faceNodeSet;
9780         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9781         while ( nodeIt->more() ) {
9782           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9783           nodeSet->insert( n );
9784           faceNodeSet.insert( n );
9785         }
9786         setOfFaceNodeSet.insert( faceNodeSet );
9787       }
9788       else if ( elem->GetType() == SMDSAbs_Volume )
9789         volSet->insert( elem );
9790     }
9791     // ------------------------------------------------------------------------------
9792     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9793     // ------------------------------------------------------------------------------
9794
9795     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9796       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9797       while ( fIt->more() ) { // loop on faces sharing a node
9798         const SMDS_MeshElement* f = fIt->next();
9799         if ( faceSet->find( f ) == faceSet->end() ) {
9800           // check if all nodes are in nodeSet and
9801           // complete setOfFaceNodeSet if they are
9802           set <const SMDS_MeshNode*> faceNodeSet;
9803           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9804           bool allInSet = true;
9805           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9806             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9807             if ( nodeSet->find( n ) == nodeSet->end() )
9808               allInSet = false;
9809             else
9810               faceNodeSet.insert( n );
9811           }
9812           if ( allInSet ) {
9813             faceSet->insert( f );
9814             setOfFaceNodeSet.insert( faceNodeSet );
9815           }
9816         }
9817       }
9818     }
9819
9820     // -------------------------------------------------------------------------
9821     // 1c. Create temporary faces representing sides of volumes if correspondent
9822     //     face does not exist
9823     // -------------------------------------------------------------------------
9824
9825     if ( !volSet->empty() ) {
9826       //int nodeSetSize = nodeSet->size();
9827
9828       // loop on given volumes
9829       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9830         SMDS_VolumeTool vol (*vIt);
9831         // loop on volume faces: find free faces
9832         // --------------------------------------
9833         list<const SMDS_MeshElement* > freeFaceList;
9834         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9835           if ( !vol.IsFreeFace( iFace ))
9836             continue;
9837           // check if there is already a face with same nodes in a face set
9838           const SMDS_MeshElement* aFreeFace = 0;
9839           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9840           int nbNodes = vol.NbFaceNodes( iFace );
9841           set <const SMDS_MeshNode*> faceNodeSet;
9842           vol.GetFaceNodes( iFace, faceNodeSet );
9843           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9844           if ( isNewFace ) {
9845             // no such a face is given but it still can exist, check it
9846             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9847             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9848           }
9849           if ( !aFreeFace ) {
9850             // create a temporary face
9851             if ( nbNodes == 3 ) {
9852               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9853               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9854             }
9855             else if ( nbNodes == 4 ) {
9856               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9857               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9858             }
9859             else {
9860               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9861               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9862               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9863             }
9864             if ( aFreeFace )
9865               tempFaceList.push_back( aFreeFace );
9866           }
9867
9868           if ( aFreeFace )
9869             freeFaceList.push_back( aFreeFace );
9870
9871         } // loop on faces of a volume
9872
9873         // choose one of several free faces of a volume
9874         // --------------------------------------------
9875         if ( freeFaceList.size() > 1 ) {
9876           // choose a face having max nb of nodes shared by other elems of a side
9877           int maxNbNodes = -1;
9878           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9879           while ( fIt != freeFaceList.end() ) { // loop on free faces
9880             int nbSharedNodes = 0;
9881             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9882             while ( nodeIt->more() ) { // loop on free face nodes
9883               const SMDS_MeshNode* n =
9884                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9885               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9886               while ( invElemIt->more() ) {
9887                 const SMDS_MeshElement* e = invElemIt->next();
9888                 nbSharedNodes += faceSet->count( e );
9889                 nbSharedNodes += elemSet->count( e );
9890               }
9891             }
9892             if ( nbSharedNodes > maxNbNodes ) {
9893               maxNbNodes = nbSharedNodes;
9894               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9895             }
9896             else if ( nbSharedNodes == maxNbNodes ) {
9897               fIt++;
9898             }
9899             else {
9900               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9901             }
9902           }
9903           if ( freeFaceList.size() > 1 )
9904           {
9905             // could not choose one face, use another way
9906             // choose a face most close to the bary center of the opposite side
9907             gp_XYZ aBC( 0., 0., 0. );
9908             set <const SMDS_MeshNode*> addedNodes;
9909             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9910             eIt = elemSet2->begin();
9911             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9912               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9913               while ( nodeIt->more() ) { // loop on free face nodes
9914                 const SMDS_MeshNode* n =
9915                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9916                 if ( addedNodes.insert( n ).second )
9917                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9918               }
9919             }
9920             aBC /= addedNodes.size();
9921             double minDist = DBL_MAX;
9922             fIt = freeFaceList.begin();
9923             while ( fIt != freeFaceList.end() ) { // loop on free faces
9924               double dist = 0;
9925               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9926               while ( nodeIt->more() ) { // loop on free face nodes
9927                 const SMDS_MeshNode* n =
9928                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9929                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9930                 dist += ( aBC - p ).SquareModulus();
9931               }
9932               if ( dist < minDist ) {
9933                 minDist = dist;
9934                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9935               }
9936               else
9937                 fIt = freeFaceList.erase( fIt++ );
9938             }
9939           }
9940         } // choose one of several free faces of a volume
9941
9942         if ( freeFaceList.size() == 1 ) {
9943           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9944           faceSet->insert( aFreeFace );
9945           // complete a node set with nodes of a found free face
9946           //           for ( iNode = 0; iNode < ; iNode++ )
9947           //             nodeSet->insert( fNodes[ iNode ] );
9948         }
9949
9950       } // loop on volumes of a side
9951
9952       //       // complete a set of faces if new nodes in a nodeSet appeared
9953       //       // ----------------------------------------------------------
9954       //       if ( nodeSetSize != nodeSet->size() ) {
9955       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9956       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9957       //           while ( fIt->more() ) { // loop on faces sharing a node
9958       //             const SMDS_MeshElement* f = fIt->next();
9959       //             if ( faceSet->find( f ) == faceSet->end() ) {
9960       //               // check if all nodes are in nodeSet and
9961       //               // complete setOfFaceNodeSet if they are
9962       //               set <const SMDS_MeshNode*> faceNodeSet;
9963       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9964       //               bool allInSet = true;
9965       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9966       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9967       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9968       //                   allInSet = false;
9969       //                 else
9970       //                   faceNodeSet.insert( n );
9971       //               }
9972       //               if ( allInSet ) {
9973       //                 faceSet->insert( f );
9974       //                 setOfFaceNodeSet.insert( faceNodeSet );
9975       //               }
9976       //             }
9977       //           }
9978       //         }
9979       //       }
9980     } // Create temporary faces, if there are volumes given
9981   } // loop on sides
9982
9983   if ( faceSet1.size() != faceSet2.size() ) {
9984     // delete temporary faces: they are in reverseElements of actual nodes
9985 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9986 //    while ( tmpFaceIt->more() )
9987 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9988 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9989 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9990 //      aMesh->RemoveElement(*tmpFaceIt);
9991     MESSAGE("Diff nb of faces");
9992     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9993   }
9994
9995   // ============================================================
9996   // 2. Find nodes to merge:
9997   //              bind a node to remove to a node to put instead
9998   // ============================================================
9999
10000   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10001   if ( theFirstNode1 != theFirstNode2 )
10002     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10003   if ( theSecondNode1 != theSecondNode2 )
10004     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10005
10006   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10007   set< long > linkIdSet; // links to process
10008   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10009
10010   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10011   list< NLink > linkList[2];
10012   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10013   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10014   // loop on links in linkList; find faces by links and append links
10015   // of the found faces to linkList
10016   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10017   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10018   {
10019     NLink link[] = { *linkIt[0], *linkIt[1] };
10020     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10021     if ( !linkIdSet.count( linkID ) )
10022       continue;
10023
10024     // by links, find faces in the face sets,
10025     // and find indices of link nodes in the found faces;
10026     // in a face set, there is only one or no face sharing a link
10027     // ---------------------------------------------------------------
10028
10029     const SMDS_MeshElement* face[] = { 0, 0 };
10030     vector<const SMDS_MeshNode*> fnodes[2];
10031     int iLinkNode[2][2];
10032     TIDSortedElemSet avoidSet;
10033     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10034       const SMDS_MeshNode* n1 = link[iSide].first;
10035       const SMDS_MeshNode* n2 = link[iSide].second;
10036       //cout << "Side " << iSide << " ";
10037       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10038       // find a face by two link nodes
10039       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10040                                                       *faceSetPtr[ iSide ], avoidSet,
10041                                                       &iLinkNode[iSide][0],
10042                                                       &iLinkNode[iSide][1] );
10043       if ( face[ iSide ])
10044       {
10045         //cout << " F " << face[ iSide]->GetID() <<endl;
10046         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10047         // put face nodes to fnodes
10048         if ( face[ iSide ]->IsQuadratic() )
10049         {
10050           // use interlaced nodes iterator
10051           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10052           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10053           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10054           while ( nIter->more() )
10055             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10056         }
10057         else
10058         {
10059           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10060                                   face[ iSide ]->end_nodes() );
10061         }
10062         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10063       }
10064     }
10065
10066     // check similarity of elements of the sides
10067     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10068       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10069       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10070         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10071       }
10072       else {
10073         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10074       }
10075       break; // do not return because it's necessary to remove tmp faces
10076     }
10077
10078     // set nodes to merge
10079     // -------------------
10080
10081     if ( face[0] && face[1] )  {
10082       const int nbNodes = face[0]->NbNodes();
10083       if ( nbNodes != face[1]->NbNodes() ) {
10084         MESSAGE("Diff nb of face nodes");
10085         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10086         break; // do not return because it s necessary to remove tmp faces
10087       }
10088       bool reverse[] = { false, false }; // order of nodes in the link
10089       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10090         // analyse link orientation in faces
10091         int i1 = iLinkNode[ iSide ][ 0 ];
10092         int i2 = iLinkNode[ iSide ][ 1 ];
10093         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10094       }
10095       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10096       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10097       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10098       {
10099         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10100                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10101       }
10102
10103       // add other links of the faces to linkList
10104       // -----------------------------------------
10105
10106       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10107         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10108         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10109         if ( !iter_isnew.second ) { // already in a set: no need to process
10110           linkIdSet.erase( iter_isnew.first );
10111         }
10112         else // new in set == encountered for the first time: add
10113         {
10114           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10115           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10116           linkList[0].push_back ( NLink( n1, n2 ));
10117           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10118         }
10119       }
10120     } // 2 faces found
10121
10122     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10123       break;
10124
10125   } // loop on link lists
10126
10127   if ( aResult == SEW_OK &&
10128        ( //linkIt[0] != linkList[0].end() ||
10129          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10130     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10131              " " << (faceSetPtr[1]->empty()));
10132     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10133   }
10134
10135   // ====================================================================
10136   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10137   // ====================================================================
10138
10139   // delete temporary faces
10140 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10141 //  while ( tmpFaceIt->more() )
10142 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10143   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10144   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10145     aMesh->RemoveElement(*tmpFaceIt);
10146
10147   if ( aResult != SEW_OK)
10148     return aResult;
10149
10150   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10151   // loop on nodes replacement map
10152   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10153   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10154     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10155       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10156       nodeIDsToRemove.push_back( nToRemove->GetID() );
10157       // loop on elements sharing nToRemove
10158       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10159       while ( invElemIt->more() ) {
10160         const SMDS_MeshElement* e = invElemIt->next();
10161         // get a new suite of nodes: make replacement
10162         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10163         vector< const SMDS_MeshNode*> nodes( nbNodes );
10164         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10165         while ( nIt->more() ) {
10166           const SMDS_MeshNode* n =
10167             static_cast<const SMDS_MeshNode*>( nIt->next() );
10168           nnIt = nReplaceMap.find( n );
10169           if ( nnIt != nReplaceMap.end() ) {
10170             nbReplaced++;
10171             n = (*nnIt).second;
10172           }
10173           nodes[ i++ ] = n;
10174         }
10175         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10176         //         elemIDsToRemove.push_back( e->GetID() );
10177         //       else
10178         if ( nbReplaced )
10179           {
10180             SMDSAbs_ElementType etyp = e->GetType();
10181             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10182             if (newElem)
10183               {
10184                 myLastCreatedElems.Append(newElem);
10185                 AddToSameGroups(newElem, e, aMesh);
10186                 int aShapeId = e->getshapeId();
10187                 if ( aShapeId )
10188                   {
10189                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10190                   }
10191               }
10192             aMesh->RemoveElement(e);
10193           }
10194       }
10195     }
10196
10197   Remove( nodeIDsToRemove, true );
10198
10199   return aResult;
10200 }
10201
10202 //================================================================================
10203 /*!
10204  * \brief Find corresponding nodes in two sets of faces
10205  * \param theSide1 - first face set
10206  * \param theSide2 - second first face
10207  * \param theFirstNode1 - a boundary node of set 1
10208  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10209  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10210  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10211  * \param nReplaceMap - output map of corresponding nodes
10212  * \return bool  - is a success or not
10213  */
10214 //================================================================================
10215
10216 #ifdef _DEBUG_
10217 //#define DEBUG_MATCHING_NODES
10218 #endif
10219
10220 SMESH_MeshEditor::Sew_Error
10221 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10222                                     set<const SMDS_MeshElement*>& theSide2,
10223                                     const SMDS_MeshNode*          theFirstNode1,
10224                                     const SMDS_MeshNode*          theFirstNode2,
10225                                     const SMDS_MeshNode*          theSecondNode1,
10226                                     const SMDS_MeshNode*          theSecondNode2,
10227                                     TNodeNodeMap &                nReplaceMap)
10228 {
10229   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10230
10231   nReplaceMap.clear();
10232   if ( theFirstNode1 != theFirstNode2 )
10233     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10234   if ( theSecondNode1 != theSecondNode2 )
10235     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10236
10237   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10238   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10239
10240   list< NLink > linkList[2];
10241   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10242   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10243
10244   // loop on links in linkList; find faces by links and append links
10245   // of the found faces to linkList
10246   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10247   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10248     NLink link[] = { *linkIt[0], *linkIt[1] };
10249     if ( linkSet.find( link[0] ) == linkSet.end() )
10250       continue;
10251
10252     // by links, find faces in the face sets,
10253     // and find indices of link nodes in the found faces;
10254     // in a face set, there is only one or no face sharing a link
10255     // ---------------------------------------------------------------
10256
10257     const SMDS_MeshElement* face[] = { 0, 0 };
10258     list<const SMDS_MeshNode*> notLinkNodes[2];
10259     //bool reverse[] = { false, false }; // order of notLinkNodes
10260     int nbNodes[2];
10261     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10262     {
10263       const SMDS_MeshNode* n1 = link[iSide].first;
10264       const SMDS_MeshNode* n2 = link[iSide].second;
10265       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10266       set< const SMDS_MeshElement* > facesOfNode1;
10267       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10268       {
10269         // during a loop of the first node, we find all faces around n1,
10270         // during a loop of the second node, we find one face sharing both n1 and n2
10271         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10272         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10273         while ( fIt->more() ) { // loop on faces sharing a node
10274           const SMDS_MeshElement* f = fIt->next();
10275           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10276               ! facesOfNode1.insert( f ).second ) // f encounters twice
10277           {
10278             if ( face[ iSide ] ) {
10279               MESSAGE( "2 faces per link " );
10280               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10281             }
10282             face[ iSide ] = f;
10283             faceSet->erase( f );
10284
10285             // get not link nodes
10286             int nbN = f->NbNodes();
10287             if ( f->IsQuadratic() )
10288               nbN /= 2;
10289             nbNodes[ iSide ] = nbN;
10290             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10291             int i1 = f->GetNodeIndex( n1 );
10292             int i2 = f->GetNodeIndex( n2 );
10293             int iEnd = nbN, iBeg = -1, iDelta = 1;
10294             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10295             if ( reverse ) {
10296               std::swap( iEnd, iBeg ); iDelta = -1;
10297             }
10298             int i = i2;
10299             while ( true ) {
10300               i += iDelta;
10301               if ( i == iEnd ) i = iBeg + iDelta;
10302               if ( i == i1 ) break;
10303               nodes.push_back ( f->GetNode( i ) );
10304             }
10305           }
10306         }
10307       }
10308     }
10309     // check similarity of elements of the sides
10310     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10311       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10312       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10313         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10314       }
10315       else {
10316         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10317       }
10318     }
10319
10320     // set nodes to merge
10321     // -------------------
10322
10323     if ( face[0] && face[1] )  {
10324       if ( nbNodes[0] != nbNodes[1] ) {
10325         MESSAGE("Diff nb of face nodes");
10326         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10327       }
10328 #ifdef DEBUG_MATCHING_NODES
10329       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10330                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10331                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10332 #endif
10333       int nbN = nbNodes[0];
10334       {
10335         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10336         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10337         for ( int i = 0 ; i < nbN - 2; ++i ) {
10338 #ifdef DEBUG_MATCHING_NODES
10339           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10340 #endif
10341           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10342         }
10343       }
10344
10345       // add other links of the face 1 to linkList
10346       // -----------------------------------------
10347
10348       const SMDS_MeshElement* f0 = face[0];
10349       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10350       for ( int i = 0; i < nbN; i++ )
10351       {
10352         const SMDS_MeshNode* n2 = f0->GetNode( i );
10353         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10354           linkSet.insert( SMESH_TLink( n1, n2 ));
10355         if ( !iter_isnew.second ) { // already in a set: no need to process
10356           linkSet.erase( iter_isnew.first );
10357         }
10358         else // new in set == encountered for the first time: add
10359         {
10360 #ifdef DEBUG_MATCHING_NODES
10361           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10362                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10363 #endif
10364           linkList[0].push_back ( NLink( n1, n2 ));
10365           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10366         }
10367         n1 = n2;
10368       }
10369     } // 2 faces found
10370   } // loop on link lists
10371
10372   return SEW_OK;
10373 }
10374
10375 //================================================================================
10376 /*!
10377  * \brief Create elements equal (on same nodes) to given ones
10378  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10379  *              elements of the uppest dimension are duplicated.
10380  */
10381 //================================================================================
10382
10383 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10384 {
10385   ClearLastCreated();
10386   SMESHDS_Mesh* mesh = GetMeshDS();
10387
10388   // get an element type and an iterator over elements
10389
10390   SMDSAbs_ElementType type;
10391   SMDS_ElemIteratorPtr elemIt;
10392   vector< const SMDS_MeshElement* > allElems;
10393   if ( theElements.empty() )
10394   {
10395     if ( mesh->NbNodes() == 0 )
10396       return;
10397     // get most complex type
10398     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10399       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10400       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10401     };
10402     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10403       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10404       {
10405         type = types[i];
10406         break;
10407       }
10408     // put all elements in the vector <allElems>
10409     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10410     elemIt = mesh->elementsIterator( type );
10411     while ( elemIt->more() )
10412       allElems.push_back( elemIt->next());
10413     elemIt = elemSetIterator( allElems );
10414   }
10415   else
10416   {
10417     type = (*theElements.begin())->GetType();
10418     elemIt = elemSetIterator( theElements );
10419   }
10420
10421   // duplicate elements
10422
10423   if ( type == SMDSAbs_Ball )
10424   {
10425     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10426     while ( elemIt->more() )
10427     {
10428       const SMDS_MeshElement* elem = elemIt->next();
10429       if ( elem->GetType() != SMDSAbs_Ball )
10430         continue;
10431       if (( elem = mesh->AddBall( elem->GetNode(0),
10432                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10433         myLastCreatedElems.Append( elem );
10434     }
10435   }
10436   else
10437   {
10438     vector< const SMDS_MeshNode* > nodes;
10439     while ( elemIt->more() )
10440     {
10441       const SMDS_MeshElement* elem = elemIt->next();
10442       if ( elem->GetType() != type )
10443         continue;
10444
10445       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10446
10447       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10448       {
10449         std::vector<int> quantities =
10450           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10451         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10452       }
10453       else
10454       {
10455         AddElement( nodes, type, elem->IsPoly() );
10456         elem = 0; // myLastCreatedElems is already filled
10457       }
10458       if ( elem )
10459         myLastCreatedElems.Append( elem );
10460     }
10461   }
10462 }
10463
10464 //================================================================================
10465 /*!
10466   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10467   \param theElems - the list of elements (edges or faces) to be replicated
10468   The nodes for duplication could be found from these elements
10469   \param theNodesNot - list of nodes to NOT replicate
10470   \param theAffectedElems - the list of elements (cells and edges) to which the
10471   replicated nodes should be associated to.
10472   \return TRUE if operation has been completed successfully, FALSE otherwise
10473 */
10474 //================================================================================
10475
10476 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10477                                     const TIDSortedElemSet& theNodesNot,
10478                                     const TIDSortedElemSet& theAffectedElems )
10479 {
10480   myLastCreatedElems.Clear();
10481   myLastCreatedNodes.Clear();
10482
10483   if ( theElems.size() == 0 )
10484     return false;
10485
10486   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10487   if ( !aMeshDS )
10488     return false;
10489
10490   bool res = false;
10491   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10492   // duplicate elements and nodes
10493   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10494   // replce nodes by duplications
10495   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10496   return res;
10497 }
10498
10499 //================================================================================
10500 /*!
10501   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10502   \param theMeshDS - mesh instance
10503   \param theElems - the elements replicated or modified (nodes should be changed)
10504   \param theNodesNot - nodes to NOT replicate
10505   \param theNodeNodeMap - relation of old node to new created node
10506   \param theIsDoubleElem - flag os to replicate element or modify
10507   \return TRUE if operation has been completed successfully, FALSE otherwise
10508 */
10509 //================================================================================
10510
10511 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10512                                     const TIDSortedElemSet& theElems,
10513                                     const TIDSortedElemSet& theNodesNot,
10514                                     std::map< const SMDS_MeshNode*,
10515                                     const SMDS_MeshNode* >& theNodeNodeMap,
10516                                     const bool theIsDoubleElem )
10517 {
10518   MESSAGE("doubleNodes");
10519   // iterate on through element and duplicate them (by nodes duplication)
10520   bool res = false;
10521   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10522   for ( ;  elemItr != theElems.end(); ++elemItr )
10523   {
10524     const SMDS_MeshElement* anElem = *elemItr;
10525     if (!anElem)
10526       continue;
10527
10528     bool isDuplicate = false;
10529     // duplicate nodes to duplicate element
10530     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10531     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10532     int ind = 0;
10533     while ( anIter->more() )
10534     {
10535
10536       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10537       SMDS_MeshNode* aNewNode = aCurrNode;
10538       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10539         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10540       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10541       {
10542         // duplicate node
10543         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10544         copyPosition( aCurrNode, aNewNode );
10545         theNodeNodeMap[ aCurrNode ] = aNewNode;
10546         myLastCreatedNodes.Append( aNewNode );
10547       }
10548       isDuplicate |= (aCurrNode != aNewNode);
10549       newNodes[ ind++ ] = aNewNode;
10550     }
10551     if ( !isDuplicate )
10552       continue;
10553
10554     if ( theIsDoubleElem )
10555       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10556     else
10557       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10558
10559     res = true;
10560   }
10561   return res;
10562 }
10563
10564 //================================================================================
10565 /*!
10566   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10567   \param theNodes - identifiers of nodes to be doubled
10568   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10569   nodes. If list of element identifiers is empty then nodes are doubled but
10570   they not assigned to elements
10571   \return TRUE if operation has been completed successfully, FALSE otherwise
10572 */
10573 //================================================================================
10574
10575 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10576                                     const std::list< int >& theListOfModifiedElems )
10577 {
10578   MESSAGE("DoubleNodes");
10579   myLastCreatedElems.Clear();
10580   myLastCreatedNodes.Clear();
10581
10582   if ( theListOfNodes.size() == 0 )
10583     return false;
10584
10585   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10586   if ( !aMeshDS )
10587     return false;
10588
10589   // iterate through nodes and duplicate them
10590
10591   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10592
10593   std::list< int >::const_iterator aNodeIter;
10594   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10595   {
10596     int aCurr = *aNodeIter;
10597     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10598     if ( !aNode )
10599       continue;
10600
10601     // duplicate node
10602
10603     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10604     if ( aNewNode )
10605     {
10606       copyPosition( aNode, aNewNode );
10607       anOldNodeToNewNode[ aNode ] = aNewNode;
10608       myLastCreatedNodes.Append( aNewNode );
10609     }
10610   }
10611
10612   // Create map of new nodes for modified elements
10613
10614   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10615
10616   std::list< int >::const_iterator anElemIter;
10617   for ( anElemIter = theListOfModifiedElems.begin();
10618         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10619   {
10620     int aCurr = *anElemIter;
10621     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10622     if ( !anElem )
10623       continue;
10624
10625     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10626
10627     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10628     int ind = 0;
10629     while ( anIter->more() )
10630     {
10631       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10632       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10633       {
10634         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10635         aNodeArr[ ind++ ] = aNewNode;
10636       }
10637       else
10638         aNodeArr[ ind++ ] = aCurrNode;
10639     }
10640     anElemToNodes[ anElem ] = aNodeArr;
10641   }
10642
10643   // Change nodes of elements
10644
10645   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10646     anElemToNodesIter = anElemToNodes.begin();
10647   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10648   {
10649     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10650     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10651     if ( anElem )
10652       {
10653       MESSAGE("ChangeElementNodes");
10654       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10655       }
10656   }
10657
10658   return true;
10659 }
10660
10661 namespace {
10662
10663   //================================================================================
10664   /*!
10665   \brief Check if element located inside shape
10666   \return TRUE if IN or ON shape, FALSE otherwise
10667   */
10668   //================================================================================
10669
10670   template<class Classifier>
10671   bool isInside(const SMDS_MeshElement* theElem,
10672                 Classifier&             theClassifier,
10673                 const double            theTol)
10674   {
10675     gp_XYZ centerXYZ (0, 0, 0);
10676     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10677     while (aNodeItr->more())
10678       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10679
10680     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10681     theClassifier.Perform(aPnt, theTol);
10682     TopAbs_State aState = theClassifier.State();
10683     return (aState == TopAbs_IN || aState == TopAbs_ON );
10684   }
10685
10686   //================================================================================
10687   /*!
10688    * \brief Classifier of the 3D point on the TopoDS_Face
10689    *        with interaface suitable for isInside()
10690    */
10691   //================================================================================
10692
10693   struct _FaceClassifier
10694   {
10695     Extrema_ExtPS       _extremum;
10696     BRepAdaptor_Surface _surface;
10697     TopAbs_State        _state;
10698
10699     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10700     {
10701       _extremum.Initialize( _surface,
10702                             _surface.FirstUParameter(), _surface.LastUParameter(),
10703                             _surface.FirstVParameter(), _surface.LastVParameter(),
10704                             _surface.Tolerance(), _surface.Tolerance() );
10705     }
10706     void Perform(const gp_Pnt& aPnt, double theTol)
10707     {
10708       theTol *= theTol;
10709       _state = TopAbs_OUT;
10710       _extremum.Perform(aPnt);
10711       if ( _extremum.IsDone() )
10712         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10713           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10714     }
10715     TopAbs_State State() const
10716     {
10717       return _state;
10718     }
10719   };
10720 }
10721
10722 //================================================================================
10723 /*!
10724   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10725   This method is the first step of DoubleNodeElemGroupsInRegion.
10726   \param theElems - list of groups of elements (edges or faces) to be replicated
10727   \param theNodesNot - list of groups of nodes not to replicated
10728   \param theShape - shape to detect affected elements (element which geometric center
10729          located on or inside shape). If the shape is null, detection is done on faces orientations
10730          (select elements with a gravity center on the side given by faces normals).
10731          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10732          The replicated nodes should be associated to affected elements.
10733   \return groups of affected elements
10734   \sa DoubleNodeElemGroupsInRegion()
10735  */
10736 //================================================================================
10737
10738 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10739                                                    const TIDSortedElemSet& theNodesNot,
10740                                                    const TopoDS_Shape&     theShape,
10741                                                    TIDSortedElemSet&       theAffectedElems)
10742 {
10743   if ( theShape.IsNull() )
10744   {
10745     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10746     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10747     std::set<const SMDS_MeshElement*> edgesToCheck;
10748     alreadyCheckedNodes.clear();
10749     alreadyCheckedElems.clear();
10750     edgesToCheck.clear();
10751
10752     // --- iterates on elements to be replicated and get elements by back references from their nodes
10753
10754     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10755     int ielem;
10756     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10757     {
10758       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10759       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10760         continue;
10761       gp_XYZ normal;
10762       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10763       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10764       std::set<const SMDS_MeshNode*> nodesElem;
10765       nodesElem.clear();
10766       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10767       while ( nodeItr->more() )
10768       {
10769         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10770         nodesElem.insert(aNode);
10771       }
10772       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10773       for (; nodit != nodesElem.end(); nodit++)
10774       {
10775         MESSAGE("  noeud ");
10776         const SMDS_MeshNode* aNode = *nodit;
10777         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10778           continue;
10779         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10780           continue;
10781         alreadyCheckedNodes.insert(aNode);
10782         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10783         while ( backElemItr->more() )
10784         {
10785           MESSAGE("    backelem ");
10786           const SMDS_MeshElement* curElem = backElemItr->next();
10787           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10788             continue;
10789           if (theElems.find(curElem) != theElems.end())
10790             continue;
10791           alreadyCheckedElems.insert(curElem);
10792           double x=0, y=0, z=0;
10793           int nb = 0;
10794           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10795           while ( nodeItr2->more() )
10796           {
10797             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10798             x += anotherNode->X();
10799             y += anotherNode->Y();
10800             z += anotherNode->Z();
10801             nb++;
10802           }
10803           gp_XYZ p;
10804           p.SetCoord( x/nb -aNode->X(),
10805                       y/nb -aNode->Y(),
10806                       z/nb -aNode->Z() );
10807           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10808           if (normal*p > 0)
10809           {
10810             MESSAGE("    --- inserted")
10811             theAffectedElems.insert( curElem );
10812           }
10813           else if (curElem->GetType() == SMDSAbs_Edge)
10814             edgesToCheck.insert(curElem);
10815         }
10816       }
10817     }
10818     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10819     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10820     for( ; eit != edgesToCheck.end(); eit++)
10821     {
10822       bool onside = true;
10823       const SMDS_MeshElement* anEdge = *eit;
10824       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10825       while ( nodeItr->more() )
10826       {
10827         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10828         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10829         {
10830           onside = false;
10831           break;
10832         }
10833       }
10834       if (onside)
10835       {
10836         MESSAGE("    --- edge onside inserted")
10837         theAffectedElems.insert(anEdge);
10838       }
10839     }
10840   }
10841   else
10842   {
10843     const double aTol = Precision::Confusion();
10844     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10845     auto_ptr<_FaceClassifier>              aFaceClassifier;
10846     if ( theShape.ShapeType() == TopAbs_SOLID )
10847     {
10848       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10849       bsc3d->PerformInfinitePoint(aTol);
10850     }
10851     else if (theShape.ShapeType() == TopAbs_FACE )
10852     {
10853       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10854     }
10855
10856     // iterates on indicated elements and get elements by back references from their nodes
10857     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10858     int ielem;
10859     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10860     {
10861       MESSAGE("element " << ielem++);
10862       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10863       if (!anElem)
10864         continue;
10865       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10866       while ( nodeItr->more() )
10867       {
10868         MESSAGE("  noeud ");
10869         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10870         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10871           continue;
10872         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10873         while ( backElemItr->more() )
10874         {
10875           MESSAGE("    backelem ");
10876           const SMDS_MeshElement* curElem = backElemItr->next();
10877           if ( curElem && theElems.find(curElem) == theElems.end() &&
10878               ( bsc3d.get() ?
10879                 isInside( curElem, *bsc3d, aTol ) :
10880                 isInside( curElem, *aFaceClassifier, aTol )))
10881             theAffectedElems.insert( curElem );
10882         }
10883       }
10884     }
10885   }
10886   return true;
10887 }
10888
10889 //================================================================================
10890 /*!
10891   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10892   \param theElems - group of of elements (edges or faces) to be replicated
10893   \param theNodesNot - group of nodes not to replicate
10894   \param theShape - shape to detect affected elements (element which geometric center
10895   located on or inside shape).
10896   The replicated nodes should be associated to affected elements.
10897   \return TRUE if operation has been completed successfully, FALSE otherwise
10898 */
10899 //================================================================================
10900
10901 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10902                                             const TIDSortedElemSet& theNodesNot,
10903                                             const TopoDS_Shape&     theShape )
10904 {
10905   if ( theShape.IsNull() )
10906     return false;
10907
10908   const double aTol = Precision::Confusion();
10909   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10910   auto_ptr<_FaceClassifier>              aFaceClassifier;
10911   if ( theShape.ShapeType() == TopAbs_SOLID )
10912   {
10913     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10914     bsc3d->PerformInfinitePoint(aTol);
10915   }
10916   else if (theShape.ShapeType() == TopAbs_FACE )
10917   {
10918     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10919   }
10920
10921   // iterates on indicated elements and get elements by back references from their nodes
10922   TIDSortedElemSet anAffected;
10923   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10924   for ( ;  elemItr != theElems.end(); ++elemItr )
10925   {
10926     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10927     if (!anElem)
10928       continue;
10929
10930     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10931     while ( nodeItr->more() )
10932     {
10933       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10934       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10935         continue;
10936       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10937       while ( backElemItr->more() )
10938       {
10939         const SMDS_MeshElement* curElem = backElemItr->next();
10940         if ( curElem && theElems.find(curElem) == theElems.end() &&
10941              ( bsc3d.get() ?
10942                isInside( curElem, *bsc3d, aTol ) :
10943                isInside( curElem, *aFaceClassifier, aTol )))
10944           anAffected.insert( curElem );
10945       }
10946     }
10947   }
10948   return DoubleNodes( theElems, theNodesNot, anAffected );
10949 }
10950
10951 /*!
10952  *  \brief compute an oriented angle between two planes defined by four points.
10953  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10954  *  @param p0 base of the rotation axe
10955  *  @param p1 extremity of the rotation axe
10956  *  @param g1 belongs to the first plane
10957  *  @param g2 belongs to the second plane
10958  */
10959 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10960 {
10961 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10962 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10963 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10964 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10965   gp_Vec vref(p0, p1);
10966   gp_Vec v1(p0, g1);
10967   gp_Vec v2(p0, g2);
10968   gp_Vec n1 = vref.Crossed(v1);
10969   gp_Vec n2 = vref.Crossed(v2);
10970   try {
10971     return n2.AngleWithRef(n1, vref);
10972   }
10973   catch ( Standard_Failure ) {
10974   }
10975   return Max( v1.Magnitude(), v2.Magnitude() );
10976 }
10977
10978 /*!
10979  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10980  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10981  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10982  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10983  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10984  * 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.
10985  * 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.
10986  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10987  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10988  * \param theElems - list of groups of volumes, where a group of volume is a set of
10989  *        SMDS_MeshElements sorted by Id.
10990  * \param createJointElems - if TRUE, create the elements
10991  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10992  *        the boundary between \a theDomains and the rest mesh
10993  * \return TRUE if operation has been completed successfully, FALSE otherwise
10994  */
10995 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10996                                                      bool                                 createJointElems,
10997                                                      bool                                 onAllBoundaries)
10998 {
10999   MESSAGE("----------------------------------------------");
11000   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11001   MESSAGE("----------------------------------------------");
11002
11003   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11004   meshDS->BuildDownWardConnectivity(true);
11005   CHRONO(50);
11006   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11007
11008   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11009   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11010   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11011
11012   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11013   std::map<int,int>celldom; // cell vtkId --> domain
11014   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11015   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11016   faceDomains.clear();
11017   celldom.clear();
11018   cellDomains.clear();
11019   nodeDomains.clear();
11020   std::map<int,int> emptyMap;
11021   std::set<int> emptySet;
11022   emptyMap.clear();
11023
11024   MESSAGE(".. Number of domains :"<<theElems.size());
11025
11026   TIDSortedElemSet theRestDomElems;
11027   const int iRestDom  = -1;
11028   const int idom0     = onAllBoundaries ? iRestDom : 0;
11029   const int nbDomains = theElems.size();
11030
11031   // Check if the domains do not share an element
11032   for (int idom = 0; idom < nbDomains-1; idom++)
11033     {
11034 //       MESSAGE("... Check of domain #" << idom);
11035       const TIDSortedElemSet& domain = theElems[idom];
11036       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11037       for (; elemItr != domain.end(); ++elemItr)
11038         {
11039           const SMDS_MeshElement* anElem = *elemItr;
11040           int idombisdeb = idom + 1 ;
11041           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11042           {
11043             const TIDSortedElemSet& domainbis = theElems[idombis];
11044             if ( domainbis.count(anElem) )
11045             {
11046               MESSAGE(".... Domain #" << idom);
11047               MESSAGE(".... Domain #" << idombis);
11048               throw SALOME_Exception("The domains are not disjoint.");
11049               return false ;
11050             }
11051           }
11052         }
11053     }
11054
11055   for (int idom = 0; idom < nbDomains; idom++)
11056     {
11057
11058       // --- build a map (face to duplicate --> volume to modify)
11059       //     with all the faces shared by 2 domains (group of elements)
11060       //     and corresponding volume of this domain, for each shared face.
11061       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11062
11063       MESSAGE("... Neighbors of domain #" << idom);
11064       const TIDSortedElemSet& domain = theElems[idom];
11065       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11066       for (; elemItr != domain.end(); ++elemItr)
11067         {
11068           const SMDS_MeshElement* anElem = *elemItr;
11069           if (!anElem)
11070             continue;
11071           int vtkId = anElem->getVtkId();
11072           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11073           int neighborsVtkIds[NBMAXNEIGHBORS];
11074           int downIds[NBMAXNEIGHBORS];
11075           unsigned char downTypes[NBMAXNEIGHBORS];
11076           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11077           for (int n = 0; n < nbNeighbors; n++)
11078             {
11079               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11080               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11081               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11082                 {
11083                   bool ok = false ;
11084                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11085                   {
11086                     // MESSAGE("Domain " << idombis);
11087                     const TIDSortedElemSet& domainbis = theElems[idombis];
11088                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11089                   }
11090                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11091                   {
11092                     DownIdType face(downIds[n], downTypes[n]);
11093                     if (!faceDomains[face].count(idom))
11094                       {
11095                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11096                         celldom[vtkId] = idom;
11097                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11098                       }
11099                     if ( !ok )
11100                     {
11101                       theRestDomElems.insert( elem );
11102                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11103                       celldom[neighborsVtkIds[n]] = iRestDom;
11104                     }
11105                   }
11106                 }
11107             }
11108         }
11109     }
11110
11111   //MESSAGE("Number of shared faces " << faceDomains.size());
11112   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11113
11114   // --- explore the shared faces domain by domain,
11115   //     explore the nodes of the face and see if they belong to a cell in the domain,
11116   //     which has only a node or an edge on the border (not a shared face)
11117
11118   for (int idomain = idom0; idomain < nbDomains; idomain++)
11119     {
11120       //MESSAGE("Domain " << idomain);
11121       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11122       itface = faceDomains.begin();
11123       for (; itface != faceDomains.end(); ++itface)
11124         {
11125           const std::map<int, int>& domvol = itface->second;
11126           if (!domvol.count(idomain))
11127             continue;
11128           DownIdType face = itface->first;
11129           //MESSAGE(" --- face " << face.cellId);
11130           std::set<int> oldNodes;
11131           oldNodes.clear();
11132           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11133           std::set<int>::iterator itn = oldNodes.begin();
11134           for (; itn != oldNodes.end(); ++itn)
11135             {
11136               int oldId = *itn;
11137               //MESSAGE("     node " << oldId);
11138               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11139               for (int i=0; i<l.ncells; i++)
11140                 {
11141                   int vtkId = l.cells[i];
11142                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11143                   if (!domain.count(anElem))
11144                     continue;
11145                   int vtkType = grid->GetCellType(vtkId);
11146                   int downId = grid->CellIdToDownId(vtkId);
11147                   if (downId < 0)
11148                     {
11149                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11150                       continue; // not OK at this stage of the algorithm:
11151                                 //no cells created after BuildDownWardConnectivity
11152                     }
11153                   DownIdType aCell(downId, vtkType);
11154                   cellDomains[aCell][idomain] = vtkId;
11155                   celldom[vtkId] = idomain;
11156                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11157                 }
11158             }
11159         }
11160     }
11161
11162   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11163   //     for each shared face, get the nodes
11164   //     for each node, for each domain of the face, create a clone of the node
11165
11166   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11167   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11168   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11169
11170   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11171   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11172   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11173
11174   MESSAGE(".. Duplication of the nodes");
11175   for (int idomain = idom0; idomain < nbDomains; idomain++)
11176     {
11177       itface = faceDomains.begin();
11178       for (; itface != faceDomains.end(); ++itface)
11179         {
11180           const std::map<int, int>& domvol = itface->second;
11181           if (!domvol.count(idomain))
11182             continue;
11183           DownIdType face = itface->first;
11184           //MESSAGE(" --- face " << face.cellId);
11185           std::set<int> oldNodes;
11186           oldNodes.clear();
11187           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11188           std::set<int>::iterator itn = oldNodes.begin();
11189           for (; itn != oldNodes.end(); ++itn)
11190             {
11191               int oldId = *itn;
11192               if (nodeDomains[oldId].empty())
11193                 {
11194                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11195                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11196                 }
11197               std::map<int, int>::const_iterator itdom = domvol.begin();
11198               for (; itdom != domvol.end(); ++itdom)
11199                 {
11200                   int idom = itdom->first;
11201                   //MESSAGE("         domain " << idom);
11202                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11203                     {
11204                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11205                         {
11206                           vector<int> orderedDoms;
11207                           //MESSAGE("multiple node " << oldId);
11208                           if (mutipleNodes.count(oldId))
11209                             orderedDoms = mutipleNodes[oldId];
11210                           else
11211                             {
11212                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11213                               for (; it != nodeDomains[oldId].end(); ++it)
11214                                 orderedDoms.push_back(it->first);
11215                             }
11216                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11217                           //stringstream txt;
11218                           //for (int i=0; i<orderedDoms.size(); i++)
11219                           //  txt << orderedDoms[i] << " ";
11220                           //MESSAGE("orderedDoms " << txt.str());
11221                           mutipleNodes[oldId] = orderedDoms;
11222                         }
11223                       double *coords = grid->GetPoint(oldId);
11224                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11225                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11226                       int newId = newNode->getVtkId();
11227                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11228                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11229                     }
11230                 }
11231             }
11232         }
11233     }
11234
11235   MESSAGE(".. Creation of elements");
11236   for (int idomain = idom0; idomain < nbDomains; idomain++)
11237     {
11238       itface = faceDomains.begin();
11239       for (; itface != faceDomains.end(); ++itface)
11240         {
11241           std::map<int, int> domvol = itface->second;
11242           if (!domvol.count(idomain))
11243             continue;
11244           DownIdType face = itface->first;
11245           //MESSAGE(" --- face " << face.cellId);
11246           std::set<int> oldNodes;
11247           oldNodes.clear();
11248           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11249           int nbMultipleNodes = 0;
11250           std::set<int>::iterator itn = oldNodes.begin();
11251           for (; itn != oldNodes.end(); ++itn)
11252             {
11253               int oldId = *itn;
11254               if (mutipleNodes.count(oldId))
11255                 nbMultipleNodes++;
11256             }
11257           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11258             {
11259               //MESSAGE("multiple Nodes detected on a shared face");
11260               int downId = itface->first.cellId;
11261               unsigned char cellType = itface->first.cellType;
11262               // --- shared edge or shared face ?
11263               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11264                 {
11265                   int nodes[3];
11266                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11267                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11268                     if (mutipleNodes.count(nodes[i]))
11269                       if (!mutipleNodesToFace.count(nodes[i]))
11270                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11271                 }
11272               else // shared face (between two volumes)
11273                 {
11274                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11275                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11276                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11277                   for (int ie =0; ie < nbEdges; ie++)
11278                     {
11279                       int nodes[3];
11280                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11281                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11282                         {
11283                           vector<int> vn0 = mutipleNodes[nodes[0]];
11284                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11285                           vector<int> doms;
11286                           for (int i0 = 0; i0 < vn0.size(); i0++)
11287                             for (int i1 = 0; i1 < vn1.size(); i1++)
11288                               if (vn0[i0] == vn1[i1])
11289                                 doms.push_back(vn0[i0]);
11290                           if (doms.size() >2)
11291                             {
11292                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11293                               double *coords = grid->GetPoint(nodes[0]);
11294                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11295                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11296                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11297                               gp_Pnt gref;
11298                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11299                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11300                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11301                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11302                               for (int id=0; id < doms.size(); id++)
11303                                 {
11304                                   int idom = doms[id];
11305                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11306                                   for (int ivol=0; ivol<nbvol; ivol++)
11307                                     {
11308                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11309                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11310                                       if (domain.count(elem))
11311                                         {
11312                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11313                                           domvol[idom] = svol;
11314                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11315                                           double values[3];
11316                                           vtkIdType npts = 0;
11317                                           vtkIdType* pts = 0;
11318                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11319                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11320                                           if (id ==0)
11321                                             {
11322                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11323                                               angleDom[idom] = 0;
11324                                             }
11325                                           else
11326                                             {
11327                                               gp_Pnt g(values[0], values[1], values[2]);
11328                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11329                                               //MESSAGE("  angle=" << angleDom[idom]);
11330                                             }
11331                                           break;
11332                                         }
11333                                     }
11334                                 }
11335                               map<double, int> sortedDom; // sort domains by angle
11336                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11337                                 sortedDom[ia->second] = ia->first;
11338                               vector<int> vnodes;
11339                               vector<int> vdom;
11340                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11341                                 {
11342                                   vdom.push_back(ib->second);
11343                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11344                                 }
11345                               for (int ino = 0; ino < nbNodes; ino++)
11346                                 vnodes.push_back(nodes[ino]);
11347                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11348                             }
11349                         }
11350                     }
11351                 }
11352             }
11353         }
11354     }
11355
11356   // --- iterate on shared faces (volumes to modify, face to extrude)
11357   //     get node id's of the face (id SMDS = id VTK)
11358   //     create flat element with old and new nodes if requested
11359
11360   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11361   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11362
11363   std::map<int, std::map<long,int> > nodeQuadDomains;
11364   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11365
11366   MESSAGE(".. Creation of elements: simple junction");
11367   if (createJointElems)
11368     {
11369       int idg;
11370       string joints2DName = "joints2D";
11371       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11372       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11373       string joints3DName = "joints3D";
11374       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11375       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11376
11377       itface = faceDomains.begin();
11378       for (; itface != faceDomains.end(); ++itface)
11379         {
11380           DownIdType face = itface->first;
11381           std::set<int> oldNodes;
11382           std::set<int>::iterator itn;
11383           oldNodes.clear();
11384           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11385
11386           std::map<int, int> domvol = itface->second;
11387           std::map<int, int>::iterator itdom = domvol.begin();
11388           int dom1 = itdom->first;
11389           int vtkVolId = itdom->second;
11390           itdom++;
11391           int dom2 = itdom->first;
11392           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11393                                                              nodeQuadDomains);
11394           stringstream grpname;
11395           grpname << "j_";
11396           if (dom1 < dom2)
11397             grpname << dom1 << "_" << dom2;
11398           else
11399             grpname << dom2 << "_" << dom1;
11400           string namegrp = grpname.str();
11401           if (!mapOfJunctionGroups.count(namegrp))
11402             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11403           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11404           if (sgrp)
11405             sgrp->Add(vol->GetID());
11406           if (vol->GetType() == SMDSAbs_Volume)
11407             joints3DGrp->Add(vol->GetID());
11408           else if (vol->GetType() == SMDSAbs_Face)
11409             joints2DGrp->Add(vol->GetID());
11410         }
11411     }
11412
11413   // --- create volumes on multiple domain intersection if requested
11414   //     iterate on mutipleNodesToFace
11415   //     iterate on edgesMultiDomains
11416
11417   MESSAGE(".. Creation of elements: multiple junction");
11418   if (createJointElems)
11419     {
11420       // --- iterate on mutipleNodesToFace
11421
11422       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11423       for (; itn != mutipleNodesToFace.end(); ++itn)
11424         {
11425           int node = itn->first;
11426           vector<int> orderDom = itn->second;
11427           vector<vtkIdType> orderedNodes;
11428           for (int idom = 0; idom <orderDom.size(); idom++)
11429             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11430             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11431
11432             stringstream grpname;
11433             grpname << "m2j_";
11434             grpname << 0 << "_" << 0;
11435             int idg;
11436             string namegrp = grpname.str();
11437             if (!mapOfJunctionGroups.count(namegrp))
11438               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11439             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11440             if (sgrp)
11441               sgrp->Add(face->GetID());
11442         }
11443
11444       // --- iterate on edgesMultiDomains
11445
11446       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11447       for (; ite != edgesMultiDomains.end(); ++ite)
11448         {
11449           vector<int> nodes = ite->first;
11450           vector<int> orderDom = ite->second;
11451           vector<vtkIdType> orderedNodes;
11452           if (nodes.size() == 2)
11453             {
11454               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11455               for (int ino=0; ino < nodes.size(); ino++)
11456                 if (orderDom.size() == 3)
11457                   for (int idom = 0; idom <orderDom.size(); idom++)
11458                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11459                 else
11460                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11461                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11462               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11463
11464               int idg;
11465               string namegrp = "jointsMultiples";
11466               if (!mapOfJunctionGroups.count(namegrp))
11467                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11468               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11469               if (sgrp)
11470                 sgrp->Add(vol->GetID());
11471             }
11472           else
11473             {
11474               INFOS("Quadratic multiple joints not implemented");
11475               // TODO quadratic nodes
11476             }
11477         }
11478     }
11479
11480   // --- list the explicit faces and edges of the mesh that need to be modified,
11481   //     i.e. faces and edges built with one or more duplicated nodes.
11482   //     associate these faces or edges to their corresponding domain.
11483   //     only the first domain found is kept when a face or edge is shared
11484
11485   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11486   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11487   faceOrEdgeDom.clear();
11488   feDom.clear();
11489
11490   MESSAGE(".. Modification of elements");
11491   for (int idomain = idom0; idomain < nbDomains; idomain++)
11492     {
11493       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11494       for (; itnod != nodeDomains.end(); ++itnod)
11495         {
11496           int oldId = itnod->first;
11497           //MESSAGE("     node " << oldId);
11498           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11499           for (int i = 0; i < l.ncells; i++)
11500             {
11501               int vtkId = l.cells[i];
11502               int vtkType = grid->GetCellType(vtkId);
11503               int downId = grid->CellIdToDownId(vtkId);
11504               if (downId < 0)
11505                 continue; // new cells: not to be modified
11506               DownIdType aCell(downId, vtkType);
11507               int volParents[1000];
11508               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11509               for (int j = 0; j < nbvol; j++)
11510                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11511                   if (!feDom.count(vtkId))
11512                     {
11513                       feDom[vtkId] = idomain;
11514                       faceOrEdgeDom[aCell] = emptyMap;
11515                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11516                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11517                       //        << " type " << vtkType << " downId " << downId);
11518                     }
11519             }
11520         }
11521     }
11522
11523   // --- iterate on shared faces (volumes to modify, face to extrude)
11524   //     get node id's of the face
11525   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11526
11527   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11528   for (int m=0; m<3; m++)
11529     {
11530       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11531       itface = (*amap).begin();
11532       for (; itface != (*amap).end(); ++itface)
11533         {
11534           DownIdType face = itface->first;
11535           std::set<int> oldNodes;
11536           std::set<int>::iterator itn;
11537           oldNodes.clear();
11538           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11539           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11540           std::map<int, int> localClonedNodeIds;
11541
11542           std::map<int, int> domvol = itface->second;
11543           std::map<int, int>::iterator itdom = domvol.begin();
11544           for (; itdom != domvol.end(); ++itdom)
11545             {
11546               int idom = itdom->first;
11547               int vtkVolId = itdom->second;
11548               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11549               localClonedNodeIds.clear();
11550               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11551                 {
11552                   int oldId = *itn;
11553                   if (nodeDomains[oldId].count(idom))
11554                     {
11555                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11556                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11557                     }
11558                 }
11559               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11560             }
11561         }
11562     }
11563
11564   // Remove empty groups (issue 0022812)
11565   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11566   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11567   {
11568     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11569       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11570   }
11571
11572   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11573   grid->BuildLinks();
11574
11575   CHRONOSTOP(50);
11576   counters::stats();
11577   return true;
11578 }
11579
11580 /*!
11581  * \brief Double nodes on some external faces and create flat elements.
11582  * Flat elements are mainly used by some types of mechanic calculations.
11583  *
11584  * Each group of the list must be constituted of faces.
11585  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11586  * @param theElems - list of groups of faces, where a group of faces is a set of
11587  * SMDS_MeshElements sorted by Id.
11588  * @return TRUE if operation has been completed successfully, FALSE otherwise
11589  */
11590 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11591 {
11592   MESSAGE("-------------------------------------------------");
11593   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11594   MESSAGE("-------------------------------------------------");
11595
11596   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11597
11598   // --- For each group of faces
11599   //     duplicate the nodes, create a flat element based on the face
11600   //     replace the nodes of the faces by their clones
11601
11602   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11603   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11604   clonedNodes.clear();
11605   intermediateNodes.clear();
11606   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11607   mapOfJunctionGroups.clear();
11608
11609   for (int idom = 0; idom < theElems.size(); idom++)
11610     {
11611       const TIDSortedElemSet& domain = theElems[idom];
11612       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11613       for (; elemItr != domain.end(); ++elemItr)
11614         {
11615           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11616           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11617           if (!aFace)
11618             continue;
11619           // MESSAGE("aFace=" << aFace->GetID());
11620           bool isQuad = aFace->IsQuadratic();
11621           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11622
11623           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11624
11625           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11626           while (nodeIt->more())
11627             {
11628               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11629               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11630               if (isMedium)
11631                 ln2.push_back(node);
11632               else
11633                 ln0.push_back(node);
11634
11635               const SMDS_MeshNode* clone = 0;
11636               if (!clonedNodes.count(node))
11637                 {
11638                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11639                   copyPosition( node, clone );
11640                   clonedNodes[node] = clone;
11641                 }
11642               else
11643                 clone = clonedNodes[node];
11644
11645               if (isMedium)
11646                 ln3.push_back(clone);
11647               else
11648                 ln1.push_back(clone);
11649
11650               const SMDS_MeshNode* inter = 0;
11651               if (isQuad && (!isMedium))
11652                 {
11653                   if (!intermediateNodes.count(node))
11654                     {
11655                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11656                       copyPosition( node, inter );
11657                       intermediateNodes[node] = inter;
11658                     }
11659                   else
11660                     inter = intermediateNodes[node];
11661                   ln4.push_back(inter);
11662                 }
11663             }
11664
11665           // --- extrude the face
11666
11667           vector<const SMDS_MeshNode*> ln;
11668           SMDS_MeshVolume* vol = 0;
11669           vtkIdType aType = aFace->GetVtkType();
11670           switch (aType)
11671           {
11672             case VTK_TRIANGLE:
11673               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11674               // MESSAGE("vol prism " << vol->GetID());
11675               ln.push_back(ln1[0]);
11676               ln.push_back(ln1[1]);
11677               ln.push_back(ln1[2]);
11678               break;
11679             case VTK_QUAD:
11680               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11681               // MESSAGE("vol hexa " << vol->GetID());
11682               ln.push_back(ln1[0]);
11683               ln.push_back(ln1[1]);
11684               ln.push_back(ln1[2]);
11685               ln.push_back(ln1[3]);
11686               break;
11687             case VTK_QUADRATIC_TRIANGLE:
11688               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11689                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11690               // MESSAGE("vol quad prism " << vol->GetID());
11691               ln.push_back(ln1[0]);
11692               ln.push_back(ln1[1]);
11693               ln.push_back(ln1[2]);
11694               ln.push_back(ln3[0]);
11695               ln.push_back(ln3[1]);
11696               ln.push_back(ln3[2]);
11697               break;
11698             case VTK_QUADRATIC_QUAD:
11699 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11700 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11701 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11702               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11703                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11704                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11705               // MESSAGE("vol quad hexa " << vol->GetID());
11706               ln.push_back(ln1[0]);
11707               ln.push_back(ln1[1]);
11708               ln.push_back(ln1[2]);
11709               ln.push_back(ln1[3]);
11710               ln.push_back(ln3[0]);
11711               ln.push_back(ln3[1]);
11712               ln.push_back(ln3[2]);
11713               ln.push_back(ln3[3]);
11714               break;
11715             case VTK_POLYGON:
11716               break;
11717             default:
11718               break;
11719           }
11720
11721           if (vol)
11722             {
11723               stringstream grpname;
11724               grpname << "jf_";
11725               grpname << idom;
11726               int idg;
11727               string namegrp = grpname.str();
11728               if (!mapOfJunctionGroups.count(namegrp))
11729                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11730               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11731               if (sgrp)
11732                 sgrp->Add(vol->GetID());
11733             }
11734
11735           // --- modify the face
11736
11737           aFace->ChangeNodes(&ln[0], ln.size());
11738         }
11739     }
11740   return true;
11741 }
11742
11743 /*!
11744  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11745  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11746  *  groups of faces to remove inside the object, (idem edges).
11747  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11748  */
11749 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11750                                       const TopoDS_Shape& theShape,
11751                                       SMESH_NodeSearcher* theNodeSearcher,
11752                                       const char* groupName,
11753                                       std::vector<double>&   nodesCoords,
11754                                       std::vector<std::vector<int> >& listOfListOfNodes)
11755 {
11756   MESSAGE("--------------------------------");
11757   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11758   MESSAGE("--------------------------------");
11759
11760   // --- zone of volumes to remove is given :
11761   //     1 either by a geom shape (one or more vertices) and a radius,
11762   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11763   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11764   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11765   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11766   //     defined by it's name.
11767
11768   SMESHDS_GroupBase* groupDS = 0;
11769   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11770   while ( groupIt->more() )
11771     {
11772       groupDS = 0;
11773       SMESH_Group * group = groupIt->next();
11774       if ( !group ) continue;
11775       groupDS = group->GetGroupDS();
11776       if ( !groupDS || groupDS->IsEmpty() ) continue;
11777       std::string grpName = group->GetName();
11778       //MESSAGE("grpName=" << grpName);
11779       if (grpName == groupName)
11780         break;
11781       else
11782         groupDS = 0;
11783     }
11784
11785   bool isNodeGroup = false;
11786   bool isNodeCoords = false;
11787   if (groupDS)
11788     {
11789       if (groupDS->GetType() != SMDSAbs_Node)
11790         return;
11791       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11792     }
11793
11794   if (nodesCoords.size() > 0)
11795     isNodeCoords = true; // a list o nodes given by their coordinates
11796   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11797
11798   // --- define groups to build
11799
11800   int idg; // --- group of SMDS volumes
11801   string grpvName = groupName;
11802   grpvName += "_vol";
11803   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11804   if (!grp)
11805     {
11806       MESSAGE("group not created " << grpvName);
11807       return;
11808     }
11809   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11810
11811   int idgs; // --- group of SMDS faces on the skin
11812   string grpsName = groupName;
11813   grpsName += "_skin";
11814   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11815   if (!grps)
11816     {
11817       MESSAGE("group not created " << grpsName);
11818       return;
11819     }
11820   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11821
11822   int idgi; // --- group of SMDS faces internal (several shapes)
11823   string grpiName = groupName;
11824   grpiName += "_internalFaces";
11825   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11826   if (!grpi)
11827     {
11828       MESSAGE("group not created " << grpiName);
11829       return;
11830     }
11831   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11832
11833   int idgei; // --- group of SMDS faces internal (several shapes)
11834   string grpeiName = groupName;
11835   grpeiName += "_internalEdges";
11836   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11837   if (!grpei)
11838     {
11839       MESSAGE("group not created " << grpeiName);
11840       return;
11841     }
11842   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11843
11844   // --- build downward connectivity
11845
11846   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11847   meshDS->BuildDownWardConnectivity(true);
11848   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11849
11850   // --- set of volumes detected inside
11851
11852   std::set<int> setOfInsideVol;
11853   std::set<int> setOfVolToCheck;
11854
11855   std::vector<gp_Pnt> gpnts;
11856   gpnts.clear();
11857
11858   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11859     {
11860       MESSAGE("group of nodes provided");
11861       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11862       while ( elemIt->more() )
11863         {
11864           const SMDS_MeshElement* elem = elemIt->next();
11865           if (!elem)
11866             continue;
11867           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11868           if (!node)
11869             continue;
11870           SMDS_MeshElement* vol = 0;
11871           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11872           while (volItr->more())
11873             {
11874               vol = (SMDS_MeshElement*)volItr->next();
11875               setOfInsideVol.insert(vol->getVtkId());
11876               sgrp->Add(vol->GetID());
11877             }
11878         }
11879     }
11880   else if (isNodeCoords)
11881     {
11882       MESSAGE("list of nodes coordinates provided");
11883       int i = 0;
11884       int k = 0;
11885       while (i < nodesCoords.size()-2)
11886         {
11887           double x = nodesCoords[i++];
11888           double y = nodesCoords[i++];
11889           double z = nodesCoords[i++];
11890           gp_Pnt p = gp_Pnt(x, y ,z);
11891           gpnts.push_back(p);
11892           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11893           k++;
11894         }
11895     }
11896   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11897     {
11898       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11899       TopTools_IndexedMapOfShape vertexMap;
11900       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11901       gp_Pnt p = gp_Pnt(0,0,0);
11902       if (vertexMap.Extent() < 1)
11903         return;
11904
11905       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11906         {
11907           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11908           p = BRep_Tool::Pnt(vertex);
11909           gpnts.push_back(p);
11910           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11911         }
11912     }
11913
11914   if (gpnts.size() > 0)
11915     {
11916       int nodeId = 0;
11917       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11918       if (startNode)
11919         nodeId = startNode->GetID();
11920       MESSAGE("nodeId " << nodeId);
11921
11922       double radius2 = radius*radius;
11923       MESSAGE("radius2 " << radius2);
11924
11925       // --- volumes on start node
11926
11927       setOfVolToCheck.clear();
11928       SMDS_MeshElement* startVol = 0;
11929       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11930       while (volItr->more())
11931         {
11932           startVol = (SMDS_MeshElement*)volItr->next();
11933           setOfVolToCheck.insert(startVol->getVtkId());
11934         }
11935       if (setOfVolToCheck.empty())
11936         {
11937           MESSAGE("No volumes found");
11938           return;
11939         }
11940
11941       // --- starting with central volumes then their neighbors, check if they are inside
11942       //     or outside the domain, until no more new neighbor volume is inside.
11943       //     Fill the group of inside volumes
11944
11945       std::map<int, double> mapOfNodeDistance2;
11946       mapOfNodeDistance2.clear();
11947       std::set<int> setOfOutsideVol;
11948       while (!setOfVolToCheck.empty())
11949         {
11950           std::set<int>::iterator it = setOfVolToCheck.begin();
11951           int vtkId = *it;
11952           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11953           bool volInside = false;
11954           vtkIdType npts = 0;
11955           vtkIdType* pts = 0;
11956           grid->GetCellPoints(vtkId, npts, pts);
11957           for (int i=0; i<npts; i++)
11958             {
11959               double distance2 = 0;
11960               if (mapOfNodeDistance2.count(pts[i]))
11961                 {
11962                   distance2 = mapOfNodeDistance2[pts[i]];
11963                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11964                 }
11965               else
11966                 {
11967                   double *coords = grid->GetPoint(pts[i]);
11968                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11969                   distance2 = 1.E40;
11970                   for (int j=0; j<gpnts.size(); j++)
11971                     {
11972                       double d2 = aPoint.SquareDistance(gpnts[j]);
11973                       if (d2 < distance2)
11974                         {
11975                           distance2 = d2;
11976                           if (distance2 < radius2)
11977                             break;
11978                         }
11979                     }
11980                   mapOfNodeDistance2[pts[i]] = distance2;
11981                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11982                 }
11983               if (distance2 < radius2)
11984                 {
11985                   volInside = true; // one or more nodes inside the domain
11986                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11987                   break;
11988                 }
11989             }
11990           if (volInside)
11991             {
11992               setOfInsideVol.insert(vtkId);
11993               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11994               int neighborsVtkIds[NBMAXNEIGHBORS];
11995               int downIds[NBMAXNEIGHBORS];
11996               unsigned char downTypes[NBMAXNEIGHBORS];
11997               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11998               for (int n = 0; n < nbNeighbors; n++)
11999                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12000                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12001             }
12002           else
12003             {
12004               setOfOutsideVol.insert(vtkId);
12005               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12006             }
12007           setOfVolToCheck.erase(vtkId);
12008         }
12009     }
12010
12011   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12012   //     If yes, add the volume to the inside set
12013
12014   bool addedInside = true;
12015   std::set<int> setOfVolToReCheck;
12016   while (addedInside)
12017     {
12018       MESSAGE(" --------------------------- re check");
12019       addedInside = false;
12020       std::set<int>::iterator itv = setOfInsideVol.begin();
12021       for (; itv != setOfInsideVol.end(); ++itv)
12022         {
12023           int vtkId = *itv;
12024           int neighborsVtkIds[NBMAXNEIGHBORS];
12025           int downIds[NBMAXNEIGHBORS];
12026           unsigned char downTypes[NBMAXNEIGHBORS];
12027           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12028           for (int n = 0; n < nbNeighbors; n++)
12029             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12030               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12031         }
12032       setOfVolToCheck = setOfVolToReCheck;
12033       setOfVolToReCheck.clear();
12034       while  (!setOfVolToCheck.empty())
12035         {
12036           std::set<int>::iterator it = setOfVolToCheck.begin();
12037           int vtkId = *it;
12038           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12039             {
12040               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12041               int countInside = 0;
12042               int neighborsVtkIds[NBMAXNEIGHBORS];
12043               int downIds[NBMAXNEIGHBORS];
12044               unsigned char downTypes[NBMAXNEIGHBORS];
12045               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12046               for (int n = 0; n < nbNeighbors; n++)
12047                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12048                   countInside++;
12049               MESSAGE("countInside " << countInside);
12050               if (countInside > 1)
12051                 {
12052                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12053                   setOfInsideVol.insert(vtkId);
12054                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12055                   addedInside = true;
12056                 }
12057               else
12058                 setOfVolToReCheck.insert(vtkId);
12059             }
12060           setOfVolToCheck.erase(vtkId);
12061         }
12062     }
12063
12064   // --- map of Downward faces at the boundary, inside the global volume
12065   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12066   //     fill group of SMDS faces inside the volume (when several volume shapes)
12067   //     fill group of SMDS faces on the skin of the global volume (if skin)
12068
12069   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12070   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12071   std::set<int>::iterator it = setOfInsideVol.begin();
12072   for (; it != setOfInsideVol.end(); ++it)
12073     {
12074       int vtkId = *it;
12075       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12076       int neighborsVtkIds[NBMAXNEIGHBORS];
12077       int downIds[NBMAXNEIGHBORS];
12078       unsigned char downTypes[NBMAXNEIGHBORS];
12079       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12080       for (int n = 0; n < nbNeighbors; n++)
12081         {
12082           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12083           if (neighborDim == 3)
12084             {
12085               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12086                 {
12087                   DownIdType face(downIds[n], downTypes[n]);
12088                   boundaryFaces[face] = vtkId;
12089                 }
12090               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12091               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12092               if (vtkFaceId >= 0)
12093                 {
12094                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12095                   // find also the smds edges on this face
12096                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12097                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12098                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12099                   for (int i = 0; i < nbEdges; i++)
12100                     {
12101                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12102                       if (vtkEdgeId >= 0)
12103                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12104                     }
12105                 }
12106             }
12107           else if (neighborDim == 2) // skin of the volume
12108             {
12109               DownIdType face(downIds[n], downTypes[n]);
12110               skinFaces[face] = vtkId;
12111               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12112               if (vtkFaceId >= 0)
12113                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12114             }
12115         }
12116     }
12117
12118   // --- identify the edges constituting the wire of each subshape on the skin
12119   //     define polylines with the nodes of edges, equivalent to wires
12120   //     project polylines on subshapes, and partition, to get geom faces
12121
12122   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12123   std::set<int> emptySet;
12124   emptySet.clear();
12125   std::set<int> shapeIds;
12126
12127   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12128   while (itelem->more())
12129     {
12130       const SMDS_MeshElement *elem = itelem->next();
12131       int shapeId = elem->getshapeId();
12132       int vtkId = elem->getVtkId();
12133       if (!shapeIdToVtkIdSet.count(shapeId))
12134         {
12135           shapeIdToVtkIdSet[shapeId] = emptySet;
12136           shapeIds.insert(shapeId);
12137         }
12138       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12139     }
12140
12141   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12142   std::set<DownIdType, DownIdCompare> emptyEdges;
12143   emptyEdges.clear();
12144
12145   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12146   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12147     {
12148       int shapeId = itShape->first;
12149       MESSAGE(" --- Shape ID --- "<< shapeId);
12150       shapeIdToEdges[shapeId] = emptyEdges;
12151
12152       std::vector<int> nodesEdges;
12153
12154       std::set<int>::iterator its = itShape->second.begin();
12155       for (; its != itShape->second.end(); ++its)
12156         {
12157           int vtkId = *its;
12158           MESSAGE("     " << vtkId);
12159           int neighborsVtkIds[NBMAXNEIGHBORS];
12160           int downIds[NBMAXNEIGHBORS];
12161           unsigned char downTypes[NBMAXNEIGHBORS];
12162           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12163           for (int n = 0; n < nbNeighbors; n++)
12164             {
12165               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12166                 continue;
12167               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12168               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12169               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12170                 {
12171                   DownIdType edge(downIds[n], downTypes[n]);
12172                   if (!shapeIdToEdges[shapeId].count(edge))
12173                     {
12174                       shapeIdToEdges[shapeId].insert(edge);
12175                       int vtkNodeId[3];
12176                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12177                       nodesEdges.push_back(vtkNodeId[0]);
12178                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12179                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12180                     }
12181                 }
12182             }
12183         }
12184
12185       std::list<int> order;
12186       order.clear();
12187       if (nodesEdges.size() > 0)
12188         {
12189           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12190           nodesEdges[0] = -1;
12191           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12192           nodesEdges[1] = -1; // do not reuse this edge
12193           bool found = true;
12194           while (found)
12195             {
12196               int nodeTofind = order.back(); // try first to push back
12197               int i = 0;
12198               for (i = 0; i<nodesEdges.size(); i++)
12199                 if (nodesEdges[i] == nodeTofind)
12200                   break;
12201               if (i == nodesEdges.size())
12202                 found = false; // no follower found on back
12203               else
12204                 {
12205                   if (i%2) // odd ==> use the previous one
12206                     if (nodesEdges[i-1] < 0)
12207                       found = false;
12208                     else
12209                       {
12210                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12211                         nodesEdges[i-1] = -1;
12212                       }
12213                   else // even ==> use the next one
12214                     if (nodesEdges[i+1] < 0)
12215                       found = false;
12216                     else
12217                       {
12218                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12219                         nodesEdges[i+1] = -1;
12220                       }
12221                 }
12222               if (found)
12223                 continue;
12224               // try to push front
12225               found = true;
12226               nodeTofind = order.front(); // try to push front
12227               for (i = 0; i<nodesEdges.size(); i++)
12228                 if (nodesEdges[i] == nodeTofind)
12229                   break;
12230               if (i == nodesEdges.size())
12231                 {
12232                   found = false; // no predecessor found on front
12233                   continue;
12234                 }
12235               if (i%2) // odd ==> use the previous one
12236                 if (nodesEdges[i-1] < 0)
12237                   found = false;
12238                 else
12239                   {
12240                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12241                     nodesEdges[i-1] = -1;
12242                   }
12243               else // even ==> use the next one
12244                 if (nodesEdges[i+1] < 0)
12245                   found = false;
12246                 else
12247                   {
12248                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12249                     nodesEdges[i+1] = -1;
12250                   }
12251             }
12252         }
12253
12254
12255       std::vector<int> nodes;
12256       nodes.push_back(shapeId);
12257       std::list<int>::iterator itl = order.begin();
12258       for (; itl != order.end(); itl++)
12259         {
12260           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12261           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12262         }
12263       listOfListOfNodes.push_back(nodes);
12264     }
12265
12266   //     partition geom faces with blocFissure
12267   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12268   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12269
12270   return;
12271 }
12272
12273
12274 //================================================================================
12275 /*!
12276  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12277  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12278  * \return TRUE if operation has been completed successfully, FALSE otherwise
12279  */
12280 //================================================================================
12281
12282 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12283 {
12284   // iterates on volume elements and detect all free faces on them
12285   SMESHDS_Mesh* aMesh = GetMeshDS();
12286   if (!aMesh)
12287     return false;
12288   //bool res = false;
12289   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12290   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12291   while(vIt->more())
12292   {
12293     const SMDS_MeshVolume* volume = vIt->next();
12294     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12295     vTool.SetExternalNormal();
12296     //const bool isPoly = volume->IsPoly();
12297     const int iQuad = volume->IsQuadratic();
12298     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12299     {
12300       if (!vTool.IsFreeFace(iface))
12301         continue;
12302       nbFree++;
12303       vector<const SMDS_MeshNode *> nodes;
12304       int nbFaceNodes = vTool.NbFaceNodes(iface);
12305       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12306       int inode = 0;
12307       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12308         nodes.push_back(faceNodes[inode]);
12309       if (iQuad) { // add medium nodes
12310         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12311           nodes.push_back(faceNodes[inode]);
12312         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12313           nodes.push_back(faceNodes[8]);
12314       }
12315       // add new face based on volume nodes
12316       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12317         nbExisted++;
12318         continue; // face already exsist
12319       }
12320       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12321       nbCreated++;
12322     }
12323   }
12324   return ( nbFree==(nbExisted+nbCreated) );
12325 }
12326
12327 namespace
12328 {
12329   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12330   {
12331     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12332       return n;
12333     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12334   }
12335 }
12336 //================================================================================
12337 /*!
12338  * \brief Creates missing boundary elements
12339  *  \param elements - elements whose boundary is to be checked
12340  *  \param dimension - defines type of boundary elements to create
12341  *  \param group - a group to store created boundary elements in
12342  *  \param targetMesh - a mesh to store created boundary elements in
12343  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12344  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12345  *                                boundary elements will be copied into the targetMesh
12346  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12347  *                                boundary elements will be added into the new group
12348  *  \param aroundElements - if true, elements will be created on boundary of given
12349  *                          elements else, on boundary of the whole mesh.
12350  * \return nb of added boundary elements
12351  */
12352 //================================================================================
12353
12354 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12355                                        Bnd_Dimension           dimension,
12356                                        SMESH_Group*            group/*=0*/,
12357                                        SMESH_Mesh*             targetMesh/*=0*/,
12358                                        bool                    toCopyElements/*=false*/,
12359                                        bool                    toCopyExistingBoundary/*=false*/,
12360                                        bool                    toAddExistingBondary/*= false*/,
12361                                        bool                    aroundElements/*= false*/)
12362 {
12363   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12364   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12365   // hope that all elements are of the same type, do not check them all
12366   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12367     throw SALOME_Exception(LOCALIZED("wrong element type"));
12368
12369   if ( !targetMesh )
12370     toCopyElements = toCopyExistingBoundary = false;
12371
12372   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12373   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12374   int nbAddedBnd = 0;
12375
12376   // editor adding present bnd elements and optionally holding elements to add to the group
12377   SMESH_MeshEditor* presentEditor;
12378   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12379   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12380
12381   SMESH_MesherHelper helper( *myMesh );
12382   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12383   SMDS_VolumeTool vTool;
12384   TIDSortedElemSet avoidSet;
12385   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12386   int inode;
12387
12388   typedef vector<const SMDS_MeshNode*> TConnectivity;
12389
12390   SMDS_ElemIteratorPtr eIt;
12391   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12392   else                  eIt = elemSetIterator( elements );
12393
12394   while (eIt->more())
12395   {
12396     const SMDS_MeshElement* elem = eIt->next();
12397     const int              iQuad = elem->IsQuadratic();
12398
12399     // ------------------------------------------------------------------------------------
12400     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12401     // ------------------------------------------------------------------------------------
12402     vector<const SMDS_MeshElement*> presentBndElems;
12403     vector<TConnectivity>           missingBndElems;
12404     TConnectivity nodes, elemNodes;
12405     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12406     {
12407       vTool.SetExternalNormal();
12408       const SMDS_MeshElement* otherVol = 0;
12409       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12410       {
12411         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12412              ( !aroundElements || elements.count( otherVol )))
12413           continue;
12414         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12415         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12416         if ( missType == SMDSAbs_Edge ) // boundary edges
12417         {
12418           nodes.resize( 2+iQuad );
12419           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12420           {
12421             for ( int j = 0; j < nodes.size(); ++j )
12422               nodes[j] =nn[i+j];
12423             if ( const SMDS_MeshElement* edge =
12424                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12425               presentBndElems.push_back( edge );
12426             else
12427               missingBndElems.push_back( nodes );
12428           }
12429         }
12430         else // boundary face
12431         {
12432           nodes.clear();
12433           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12434             nodes.push_back( nn[inode] ); // add corner nodes
12435           if (iQuad)
12436             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12437               nodes.push_back( nn[inode] ); // add medium nodes
12438           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12439           if ( iCenter > 0 )
12440             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12441
12442           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12443                                                                SMDSAbs_Face, /*noMedium=*/false ))
12444             presentBndElems.push_back( f );
12445           else
12446             missingBndElems.push_back( nodes );
12447
12448           if ( targetMesh != myMesh )
12449           {
12450             // add 1D elements on face boundary to be added to a new mesh
12451             const SMDS_MeshElement* edge;
12452             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12453             {
12454               if ( iQuad )
12455                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12456               else
12457                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12458               if ( edge && avoidSet.insert( edge ).second )
12459                 presentBndElems.push_back( edge );
12460             }
12461           }
12462         }
12463       }
12464     }
12465     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12466     {
12467       avoidSet.clear(), avoidSet.insert( elem );
12468       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12469                         SMDS_MeshElement::iterator() );
12470       elemNodes.push_back( elemNodes[0] );
12471       nodes.resize( 2 + iQuad );
12472       const int nbLinks = elem->NbCornerNodes();
12473       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12474       {
12475         nodes[0] = elemNodes[iN];
12476         nodes[1] = elemNodes[iN+1+iQuad];
12477         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12478           continue; // not free link
12479
12480         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12481         if ( const SMDS_MeshElement* edge =
12482              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12483           presentBndElems.push_back( edge );
12484         else
12485           missingBndElems.push_back( nodes );
12486       }
12487     }
12488
12489     // ---------------------------------
12490     // 2. Add missing boundary elements
12491     // ---------------------------------
12492     if ( targetMesh != myMesh )
12493       // instead of making a map of nodes in this mesh and targetMesh,
12494       // we create nodes with same IDs.
12495       for ( int i = 0; i < missingBndElems.size(); ++i )
12496       {
12497         TConnectivity& srcNodes = missingBndElems[i];
12498         TConnectivity  nodes( srcNodes.size() );
12499         for ( inode = 0; inode < nodes.size(); ++inode )
12500           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12501         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12502                                                                    missType,
12503                                                                    /*noMedium=*/false))
12504           continue;
12505         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12506         ++nbAddedBnd;
12507       }
12508     else
12509       for ( int i = 0; i < missingBndElems.size(); ++i )
12510       {
12511         TConnectivity& nodes = missingBndElems[i];
12512         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12513                                                                    missType,
12514                                                                    /*noMedium=*/false))
12515           continue;
12516         SMDS_MeshElement* elem =
12517           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12518         ++nbAddedBnd;
12519
12520         // try to set a new element to a shape
12521         if ( myMesh->HasShapeToMesh() )
12522         {
12523           bool ok = true;
12524           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12525           const int nbN = nodes.size() / (iQuad+1 );
12526           for ( inode = 0; inode < nbN && ok; ++inode )
12527           {
12528             pair<int, TopAbs_ShapeEnum> i_stype =
12529               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12530             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12531               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12532           }
12533           if ( ok && mediumShapes.size() > 1 )
12534           {
12535             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12536             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12537             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12538             {
12539               if (( ok = ( stype_i->first != stype_i_0.first )))
12540                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12541                                         aMesh->IndexToShape( stype_i_0.second ));
12542             }
12543           }
12544           if ( ok && mediumShapes.begin()->first == missShapeType )
12545             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12546         }
12547       }
12548
12549     // ----------------------------------
12550     // 3. Copy present boundary elements
12551     // ----------------------------------
12552     if ( toCopyExistingBoundary )
12553       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12554       {
12555         const SMDS_MeshElement* e = presentBndElems[i];
12556         TConnectivity nodes( e->NbNodes() );
12557         for ( inode = 0; inode < nodes.size(); ++inode )
12558           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12559         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12560       }
12561     else // store present elements to add them to a group
12562       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12563       {
12564         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12565       }
12566
12567   } // loop on given elements
12568
12569   // ---------------------------------------------
12570   // 4. Fill group with boundary elements
12571   // ---------------------------------------------
12572   if ( group )
12573   {
12574     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12575       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12576         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12577   }
12578   tgtEditor.myLastCreatedElems.Clear();
12579   tgtEditor2.myLastCreatedElems.Clear();
12580
12581   // -----------------------
12582   // 5. Copy given elements
12583   // -----------------------
12584   if ( toCopyElements && targetMesh != myMesh )
12585   {
12586     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12587     else                  eIt = elemSetIterator( elements );
12588     while (eIt->more())
12589     {
12590       const SMDS_MeshElement* elem = eIt->next();
12591       TConnectivity nodes( elem->NbNodes() );
12592       for ( inode = 0; inode < nodes.size(); ++inode )
12593         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12594       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12595
12596       tgtEditor.myLastCreatedElems.Clear();
12597     }
12598   }
12599   return nbAddedBnd;
12600 }
12601
12602 //================================================================================
12603 /*!
12604  * \brief Copy node position and set \a to node on the same geometry
12605  */
12606 //================================================================================
12607
12608 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12609                                      const SMDS_MeshNode* to )
12610 {
12611   if ( !from || !to ) return;
12612
12613   SMDS_PositionPtr pos = from->GetPosition();
12614   if ( !pos || from->getshapeId() < 1 ) return;
12615
12616   switch ( pos->GetTypeOfPosition() )
12617   {
12618   case SMDS_TOP_3DSPACE: break;
12619
12620   case SMDS_TOP_FACE:
12621   {
12622     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12623     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12624                                 fPos->GetUParameter(), fPos->GetVParameter() );
12625     break;
12626   }
12627   case SMDS_TOP_EDGE:
12628   {
12629     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12630     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12631     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12632     break;
12633   }
12634   case SMDS_TOP_VERTEX:
12635   {
12636     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12637     break;
12638   }
12639   case SMDS_TOP_UNSPEC:
12640   default:;
12641   }
12642 }