Salome HOME
BUG: EDF 2655: Hexa splitting into tetra low performance
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2013  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.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9] );
229       }
230       else if (nbnode == 12) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10], node[11], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12] );
247       }
248       else if (nbnode == 15) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523     for ( ; id_sm != id2sm.end(); ++id_sm )
524       if ( id_sm->second->Contains( theElem ))
525         return id_sm->first;
526   }
527
528   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
529   return 0;
530 }
531
532 //=======================================================================
533 //function : IsMedium
534 //purpose  :
535 //=======================================================================
536
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
538                                 const SMDSAbs_ElementType typeToCheck)
539 {
540   bool isMedium = false;
541   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542   while (it->more() && !isMedium ) {
543     const SMDS_MeshElement* elem = it->next();
544     isMedium = elem->IsMediumNode(node);
545   }
546   return isMedium;
547 }
548
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose  : Shift nodes in the array corresponded to quadratic triangle
552 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
554
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 {
557   const SMDS_MeshNode* nd1 = aNodes[0];
558   aNodes[0] = aNodes[1];
559   aNodes[1] = aNodes[2];
560   aNodes[2] = nd1;
561   const SMDS_MeshNode* nd2 = aNodes[3];
562   aNodes[3] = aNodes[4];
563   aNodes[4] = aNodes[5];
564   aNodes[5] = nd2;
565 }
566
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose  : return number of the edges connected with the theNode.
570 //           if theEdges has connections with the other type of the
571 //           elements, return -1
572 //=======================================================================
573
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 {
576   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577   // int nb=0;
578   // while(elemIt->more()) {
579   //   elemIt->next();
580   //   nb++;
581   // }
582   // return nb;
583   return theNode->NbInverseElements();
584 }
585
586 //=======================================================================
587 //function : getNodesFromTwoTria
588 //purpose  : 
589 //=======================================================================
590
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592                                 const SMDS_MeshElement * theTria2,
593                                 vector< const SMDS_MeshNode*>& N1,
594                                 vector< const SMDS_MeshNode*>& N2)
595 {
596   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597   if ( N1.size() < 6 ) return false;
598   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599   if ( N2.size() < 6 ) return false;
600
601   int sames[3] = {-1,-1,-1};
602   int nbsames = 0;
603   int i, j;
604   for(i=0; i<3; i++) {
605     for(j=0; j<3; j++) {
606       if(N1[i]==N2[j]) {
607         sames[i] = j;
608         nbsames++;
609         break;
610       }
611     }
612   }
613   if(nbsames!=2) return false;
614   if(sames[0]>-1) {
615     shiftNodesQuadTria(N1);
616     if(sames[1]>-1) {
617       shiftNodesQuadTria(N1);
618     }
619   }
620   i = sames[0] + sames[1] + sames[2];
621   for(; i<2; i++) {
622     shiftNodesQuadTria(N2);
623   }
624   // now we receive following N1 and N2 (using numeration as in the image below)
625   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
626   // i.e. first nodes from both arrays form a new diagonal
627   return true;
628 }
629
630 //=======================================================================
631 //function : InverseDiag
632 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
633 //           but having other common link.
634 //           Return False if args are improper
635 //=======================================================================
636
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638                                     const SMDS_MeshElement * theTria2 )
639 {
640   MESSAGE("InverseDiag");
641   myLastCreatedElems.Clear();
642   myLastCreatedNodes.Clear();
643
644   if (!theTria1 || !theTria2)
645     return false;
646
647   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648   if (!F1) return false;
649   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650   if (!F2) return false;
651   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653
654     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
656     //    |/ |                                         | \|
657     //  B +--+ 2                                     B +--+ 2
658
659     // put nodes in array and find out indices of the same ones
660     const SMDS_MeshNode* aNodes [6];
661     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
662     int i = 0;
663     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664     while ( it->more() ) {
665       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666
667       if ( i > 2 ) // theTria2
668         // find same node of theTria1
669         for ( int j = 0; j < 3; j++ )
670           if ( aNodes[ i ] == aNodes[ j ]) {
671             sameInd[ j ] = i;
672             sameInd[ i ] = j;
673             break;
674           }
675       // next
676       i++;
677       if ( i == 3 ) {
678         if ( it->more() )
679           return false; // theTria1 is not a triangle
680         it = theTria2->nodesIterator();
681       }
682       if ( i == 6 && it->more() )
683         return false; // theTria2 is not a triangle
684     }
685
686     // find indices of 1,2 and of A,B in theTria1
687     int iA = 0, iB = 0, i1 = 0, i2 = 0;
688     for ( i = 0; i < 6; i++ ) {
689       if ( sameInd [ i ] == 0 ) {
690         if ( i < 3 ) i1 = i;
691         else         i2 = i;
692       }
693       else if (i < 3) {
694         if ( iA ) iB = i;
695         else      iA = i;
696       }
697     }
698     // nodes 1 and 2 should not be the same
699     if ( aNodes[ i1 ] == aNodes[ i2 ] )
700       return false;
701
702     // theTria1: A->2
703     aNodes[ iA ] = aNodes[ i2 ];
704     // theTria2: B->1
705     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706
707     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
709
710     return true;
711
712   } // end if(F1 && F2)
713
714   // check case of quadratic faces
715   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717     return false;
718   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
720     return false;
721
722   //       5
723   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
725   //    |   / |
726   //  7 +  +  + 6
727   //    | /9  |
728   //    |/    |
729   //  4 +--+--+ 3
730   //       8
731
732   vector< const SMDS_MeshNode* > N1;
733   vector< const SMDS_MeshNode* > N2;
734   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735     return false;
736   // now we receive following N1 and N2 (using numeration as above image)
737   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
738   // i.e. first nodes from both arrays determ new diagonal
739
740   vector< const SMDS_MeshNode*> N1new( N1.size() );
741   vector< const SMDS_MeshNode*> N2new( N2.size() );
742   N1new.back() = N1.back(); // central node of biquadratic
743   N2new.back() = N2.back();
744   N1new[0] = N1[0];  N2new[0] = N1[0];
745   N1new[1] = N2[0];  N2new[1] = N1[1];
746   N1new[2] = N2[1];  N2new[2] = N2[0];
747   N1new[3] = N1[4];  N2new[3] = N1[3];
748   N1new[4] = N2[3];  N2new[4] = N2[5];
749   N1new[5] = N1[5];  N2new[5] = N1[4];
750   // change nodes in faces
751   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753
754   // move the central node of biquadratic triangle
755   SMESH_MesherHelper helper( *GetMesh() );
756   for ( int is2nd = 0; is2nd < 2; ++is2nd )
757   {
758     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
759     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760     if ( nodes.size() < 7 )
761       continue;
762     helper.SetSubShape( tria->getshapeId() );
763     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
764     gp_Pnt xyz;
765     if ( F.IsNull() )
766     {
767       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768               SMESH_TNodeXYZ( nodes[4] ) +
769               SMESH_TNodeXYZ( nodes[5] )) / 3.;
770     }
771     else
772     {
773       bool checkUV;
774       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777       TopLoc_Location loc;
778       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779       xyz = S->Value( uv.X(), uv.Y() );
780       xyz.Transform( loc );
781       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
782            nodes[6]->getshapeId() > 0 )
783         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784     }
785     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
786   }
787   return true;
788 }
789
790 //=======================================================================
791 //function : findTriangles
792 //purpose  : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
794
795 static bool findTriangles(const SMDS_MeshNode *    theNode1,
796                           const SMDS_MeshNode *    theNode2,
797                           const SMDS_MeshElement*& theTria1,
798                           const SMDS_MeshElement*& theTria2)
799 {
800   if ( !theNode1 || !theNode2 ) return false;
801
802   theTria1 = theTria2 = 0;
803
804   set< const SMDS_MeshElement* > emap;
805   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806   while (it->more()) {
807     const SMDS_MeshElement* elem = it->next();
808     if ( elem->NbCornerNodes() == 3 )
809       emap.insert( elem );
810   }
811   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812   while (it->more()) {
813     const SMDS_MeshElement* elem = it->next();
814     if ( emap.count( elem )) {
815       if ( !theTria1 )
816       {
817         theTria1 = elem;
818       }
819       else  
820       {
821         theTria2 = elem;
822         // theTria1 must be element with minimum ID
823         if ( theTria2->GetID() < theTria1->GetID() )
824           std::swap( theTria2, theTria1 );
825         return true;
826       }
827     }
828   }
829   return false;
830 }
831
832 //=======================================================================
833 //function : InverseDiag
834 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
835 //           with ones built on the same 4 nodes but having other common link.
836 //           Return false if proper faces not found
837 //=======================================================================
838
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840                                     const SMDS_MeshNode * theNode2)
841 {
842   myLastCreatedElems.Clear();
843   myLastCreatedNodes.Clear();
844
845   MESSAGE( "::InverseDiag()" );
846
847   const SMDS_MeshElement *tr1, *tr2;
848   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849     return false;
850
851   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852   if (!F1) return false;
853   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854   if (!F2) return false;
855   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857
858     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
860     //    |/ |                                    | \|
861     //  B +--+ 2                                B +--+ 2
862
863     // put nodes in array
864     // and find indices of 1,2 and of A in tr1 and of B in tr2
865     int i, iA1 = 0, i1 = 0;
866     const SMDS_MeshNode* aNodes1 [3];
867     SMDS_ElemIteratorPtr it;
868     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870       if ( aNodes1[ i ] == theNode1 )
871         iA1 = i; // node A in tr1
872       else if ( aNodes1[ i ] != theNode2 )
873         i1 = i;  // node 1
874     }
875     int iB2 = 0, i2 = 0;
876     const SMDS_MeshNode* aNodes2 [3];
877     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879       if ( aNodes2[ i ] == theNode2 )
880         iB2 = i; // node B in tr2
881       else if ( aNodes2[ i ] != theNode1 )
882         i2 = i;  // node 2
883     }
884
885     // nodes 1 and 2 should not be the same
886     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
887       return false;
888
889     // tr1: A->2
890     aNodes1[ iA1 ] = aNodes2[ i2 ];
891     // tr2: B->1
892     aNodes2[ iB2 ] = aNodes1[ i1 ];
893
894     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
896
897     return true;
898   }
899
900   // check case of quadratic faces
901   return InverseDiag(tr1,tr2);
902 }
903
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
907 //           fusion of triangles tr1 and tr2 having shared link on
908 //           theNode1 and theNode2
909 //=======================================================================
910
911 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
912                         const SMDS_MeshNode *    theNode1,
913                         const SMDS_MeshNode *    theNode2,
914                         const SMDS_MeshElement * tr1,
915                         const SMDS_MeshElement * tr2 )
916 {
917   if( tr1->NbNodes() != tr2->NbNodes() )
918     return false;
919   // find the 4-th node to insert into tr1
920   const SMDS_MeshNode* n4 = 0;
921   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922   int i=0;
923   while ( !n4 && i<3 ) {
924     const SMDS_MeshNode * n = cast2Node( it->next() );
925     i++;
926     bool isDiag = ( n == theNode1 || n == theNode2 );
927     if ( !isDiag )
928       n4 = n;
929   }
930   // Make an array of nodes to be in a quadrangle
931   int iNode = 0, iFirstDiag = -1;
932   it = tr1->nodesIterator();
933   i=0;
934   while ( i<3 ) {
935     const SMDS_MeshNode * n = cast2Node( it->next() );
936     i++;
937     bool isDiag = ( n == theNode1 || n == theNode2 );
938     if ( isDiag ) {
939       if ( iFirstDiag < 0 )
940         iFirstDiag = iNode;
941       else if ( iNode - iFirstDiag == 1 )
942         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943     }
944     else if ( n == n4 ) {
945       return false; // tr1 and tr2 should not have all the same nodes
946     }
947     theQuadNodes[ iNode++ ] = n;
948   }
949   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950     theQuadNodes[ iNode ] = n4;
951
952   return true;
953 }
954
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
958 //           with a quadrangle built on the same 4 nodes.
959 //           Return false if proper faces not found
960 //=======================================================================
961
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963                                    const SMDS_MeshNode * theNode2)
964 {
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   MESSAGE( "::DeleteDiag()" );
969
970   const SMDS_MeshElement *tr1, *tr2;
971   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972     return false;
973
974   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975   if (!F1) return false;
976   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977   if (!F2) return false;
978   SMESHDS_Mesh * aMesh = GetMeshDS();
979
980   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982
983     const SMDS_MeshNode* aNodes [ 4 ];
984     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985       return false;
986
987     const SMDS_MeshElement* newElem = 0;
988     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989     myLastCreatedElems.Append(newElem);
990     AddToSameGroups( newElem, tr1, aMesh );
991     int aShapeId = tr1->getshapeId();
992     if ( aShapeId )
993       {
994         aMesh->SetMeshElementOnShape( newElem, aShapeId );
995       }
996     aMesh->RemoveElement( tr1 );
997     aMesh->RemoveElement( tr2 );
998
999     return true;
1000   }
1001
1002   // check case of quadratic faces
1003   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1006     return false;
1007
1008   //       5
1009   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1011   //    |   / |
1012   //  7 +  +  + 6
1013   //    | /9  |
1014   //    |/    |
1015   //  4 +--+--+ 3
1016   //       8
1017
1018   vector< const SMDS_MeshNode* > N1;
1019   vector< const SMDS_MeshNode* > N2;
1020   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021     return false;
1022   // now we receive following N1 and N2 (using numeration as above image)
1023   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1024   // i.e. first nodes from both arrays determ new diagonal
1025
1026   const SMDS_MeshNode* aNodes[8];
1027   aNodes[0] = N1[0];
1028   aNodes[1] = N1[1];
1029   aNodes[2] = N2[0];
1030   aNodes[3] = N2[1];
1031   aNodes[4] = N1[3];
1032   aNodes[5] = N2[5];
1033   aNodes[6] = N2[3];
1034   aNodes[7] = N1[5];
1035
1036   const SMDS_MeshElement* newElem = 0;
1037   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039   myLastCreatedElems.Append(newElem);
1040   AddToSameGroups( newElem, tr1, aMesh );
1041   int aShapeId = tr1->getshapeId();
1042   if ( aShapeId )
1043     {
1044       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045     }
1046   aMesh->RemoveElement( tr1 );
1047   aMesh->RemoveElement( tr2 );
1048
1049   // remove middle node (9)
1050   GetMeshDS()->RemoveNode( N1[4] );
1051
1052   return true;
1053 }
1054
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose  : Reverse theElement orientation
1058 //=======================================================================
1059
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 {
1062   MESSAGE("Reorient");
1063   myLastCreatedElems.Clear();
1064   myLastCreatedNodes.Clear();
1065
1066   if (!theElem)
1067     return false;
1068   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069   if ( !it || !it->more() )
1070     return false;
1071
1072   const SMDSAbs_ElementType type = theElem->GetType();
1073   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074     return false;
1075
1076   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078   {
1079     const SMDS_VtkVolume* aPolyedre =
1080       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081     if (!aPolyedre) {
1082       MESSAGE("Warning: bad volumic element");
1083       return false;
1084     }
1085     const int nbFaces = aPolyedre->NbFaces();
1086     vector<const SMDS_MeshNode *> poly_nodes;
1087     vector<int> quantities (nbFaces);
1088
1089     // reverse each face of the polyedre
1090     for (int iface = 1; iface <= nbFaces; iface++) {
1091       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092       quantities[iface - 1] = nbFaceNodes;
1093
1094       for (inode = nbFaceNodes; inode >= 1; inode--) {
1095         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096         poly_nodes.push_back(curNode);
1097       }
1098     }
1099     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100   }
1101   else // other elements
1102   {
1103     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105     if ( interlace.empty() )
1106     {
1107       std::reverse( nodes.begin(), nodes.end() ); // polygon
1108     }
1109     else if ( interlace.size() > 1 )
1110     {
1111       SMDS_MeshCell::applyInterlace( interlace, nodes );
1112     }
1113     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1114   }
1115   return false;
1116 }
1117
1118 //================================================================================
1119 /*!
1120  * \brief Reorient faces.
1121  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122  * \param theDirection - desired direction of normal of \a theFace
1123  * \param theFace - one of \a theFaces that sould be oriented according to
1124  *        \a theDirection and whose orientation defines orientation of other faces
1125  * \return number of reoriented faces.
1126  */
1127 //================================================================================
1128
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1130                                   const gp_Dir&            theDirection,
1131                                   const SMDS_MeshElement * theFace)
1132 {
1133   int nbReori = 0;
1134   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135
1136   if ( theFaces.empty() )
1137   {
1138     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139     while ( fIt->more() )
1140       theFaces.insert( theFaces.end(), fIt->next() );
1141   }
1142
1143   // orient theFace according to theDirection
1144   gp_XYZ normal;
1145   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146   if ( normal * theDirection.XYZ() < 0 )
1147     nbReori += Reorient( theFace );
1148
1149   // Orient other faces
1150
1151   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152   TIDSortedElemSet avoidSet;
1153   set< SMESH_TLink > checkedLinks;
1154   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155
1156   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157     theFaces.erase( theFace );
1158   startFaces.insert( theFace );
1159
1160   int nodeInd1, nodeInd2;
1161   const SMDS_MeshElement*           otherFace;
1162   vector< const SMDS_MeshElement* > facesNearLink;
1163   vector< std::pair< int, int > >   nodeIndsOfFace;
1164
1165   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166   while ( !startFaces.empty() )
1167   {
1168     startFace = startFaces.begin();
1169     theFace = *startFace;
1170     startFaces.erase( startFace );
1171     if ( !visitedFaces.insert( theFace ).second )
1172       continue;
1173
1174     avoidSet.clear();
1175     avoidSet.insert(theFace);
1176
1177     NLink link( theFace->GetNode( 0 ), 0 );
1178
1179     const int nbNodes = theFace->NbCornerNodes();
1180     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181     {
1182       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183       linkIt_isNew = checkedLinks.insert( link );
1184       if ( !linkIt_isNew.second )
1185       {
1186         // link has already been checked and won't be encountered more
1187         // if the group (theFaces) is manifold
1188         //checkedLinks.erase( linkIt_isNew.first );
1189       }
1190       else
1191       {
1192         facesNearLink.clear();
1193         nodeIndsOfFace.clear();
1194         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195                                                              theFaces, avoidSet,
1196                                                              &nodeInd1, &nodeInd2 )))
1197           if ( otherFace != theFace)
1198           {
1199             facesNearLink.push_back( otherFace );
1200             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201             avoidSet.insert( otherFace );
1202           }
1203         if ( facesNearLink.size() > 1 )
1204         {
1205           // NON-MANIFOLD mesh shell !
1206           // select a face most co-directed with theFace,
1207           // other faces won't be visited this time
1208           gp_XYZ NF, NOF;
1209           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210           double proj, maxProj = -1;
1211           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213             if (( proj = Abs( NF * NOF )) > maxProj ) {
1214               maxProj = proj;
1215               otherFace = facesNearLink[i];
1216               nodeInd1  = nodeIndsOfFace[i].first;
1217               nodeInd2  = nodeIndsOfFace[i].second;
1218             }
1219           }
1220           // not to visit rejected faces
1221           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223               visitedFaces.insert( facesNearLink[i] );
1224         }
1225         else if ( facesNearLink.size() == 1 )
1226         {
1227           otherFace = facesNearLink[0];
1228           nodeInd1  = nodeIndsOfFace.back().first;
1229           nodeInd2  = nodeIndsOfFace.back().second;
1230         }
1231         if ( otherFace && otherFace != theFace)
1232         {
1233           // link must be reverse in otherFace if orientation ot otherFace
1234           // is same as that of theFace
1235           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236           {
1237             nbReori += Reorient( otherFace );
1238           }
1239           startFaces.insert( otherFace );
1240         }
1241       }
1242       std::swap( link.first, link.second ); // reverse the link
1243     }
1244   }
1245   return nbReori;
1246 }
1247
1248 //=======================================================================
1249 //function : getBadRate
1250 //purpose  :
1251 //=======================================================================
1252
1253 static double getBadRate (const SMDS_MeshElement*               theElem,
1254                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1255 {
1256   SMESH::Controls::TSequenceOfXYZ P;
1257   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1258     return 1e100;
1259   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 }
1262
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose  : Cut quadrangles into triangles.
1266 //           theCrit is used to select a diagonal to cut
1267 //=======================================================================
1268
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1270                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1271 {
1272   myLastCreatedElems.Clear();
1273   myLastCreatedNodes.Clear();
1274
1275   if ( !theCrit.get() )
1276     return false;
1277
1278   SMESHDS_Mesh * aMesh = GetMeshDS();
1279
1280   Handle(Geom_Surface) surface;
1281   SMESH_MesherHelper   helper( *GetMesh() );
1282
1283   TIDSortedElemSet::iterator itElem;
1284   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1285   {
1286     const SMDS_MeshElement* elem = *itElem;
1287     if ( !elem || elem->GetType() != SMDSAbs_Face )
1288       continue;
1289     if ( elem->NbCornerNodes() != 4 )
1290       continue;
1291
1292     // retrieve element nodes
1293     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1294
1295     // compare two sets of possible triangles
1296     double aBadRate1, aBadRate2; // to what extent a set is bad
1297     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1300
1301     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1304
1305     const int aShapeId = FindShape( elem );
1306     const SMDS_MeshElement* newElem1 = 0;
1307     const SMDS_MeshElement* newElem2 = 0;
1308
1309     if ( !elem->IsQuadratic() ) // split liner quadrangle
1310     {
1311       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313       if ( aBadRate1 <= aBadRate2 ) {
1314         // tr1 + tr2 is better
1315         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317       }
1318       else {
1319         // tr3 + tr4 is better
1320         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322       }
1323     }
1324     else // split quadratic quadrangle
1325     {
1326       helper.SetIsQuadratic( true );
1327       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1328
1329       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330       if ( aNodes.size() == 9 )
1331       {
1332         helper.SetIsBiQuadratic( true );
1333         if ( aBadRate1 <= aBadRate2 )
1334           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1335         else
1336           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1337       }
1338       // create a new element
1339       if ( aBadRate1 <= aBadRate2 ) {
1340         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342       }
1343       else {
1344         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1360
1361     aMesh->RemoveElement( elem );
1362   }
1363   return true;
1364 }
1365
1366 //=======================================================================
1367 /*!
1368  * \brief Split each of given quadrangles into 4 triangles.
1369  * \param theElems - The faces to be splitted. If empty all faces are split.
1370  */
1371 //=======================================================================
1372
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   SMESH_MesherHelper helper( *GetMesh() );
1379   helper.SetElementsOnShape( true );
1380
1381   SMDS_ElemIteratorPtr faceIt;
1382   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383   else                    faceIt = elemSetIterator( theElems );
1384
1385   bool   checkUV;
1386   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1387   gp_XYZ xyz[9];
1388   vector< const SMDS_MeshNode* > nodes;
1389   SMESHDS_SubMesh*               subMeshDS;
1390   TopoDS_Face                    F;
1391   Handle(Geom_Surface)           surface;
1392   TopLoc_Location                loc;
1393
1394   while ( faceIt->more() )
1395   {
1396     const SMDS_MeshElement* quad = faceIt->next();
1397     if ( !quad || quad->NbCornerNodes() != 4 )
1398       continue;
1399
1400     // get a surface the quad is on
1401
1402     if ( quad->getshapeId() < 1 )
1403     {
1404       F.Nullify();
1405       helper.SetSubShape( 0 );
1406       subMeshDS = 0;
1407     }
1408     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1409     {
1410       helper.SetSubShape( quad->getshapeId() );
1411       if ( !helper.GetSubShape().IsNull() &&
1412            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1413       {
1414         F = TopoDS::Face( helper.GetSubShape() );
1415         surface = BRep_Tool::Surface( F, loc );
1416         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1417       }
1418       else
1419       {
1420         helper.SetSubShape( 0 );
1421         subMeshDS = 0;
1422       }
1423     }
1424
1425     // create a central node
1426
1427     const SMDS_MeshNode* nCentral;
1428     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1429
1430     if ( nodes.size() == 9 )
1431     {
1432       nCentral = nodes.back();
1433     }
1434     else
1435     {
1436       size_t iN = 0;
1437       if ( F.IsNull() )
1438       {
1439         for ( ; iN < nodes.size(); ++iN )
1440           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1441
1442         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1444
1445         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446                                    xyz[0], xyz[1], xyz[2], xyz[3],
1447                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1448       }
1449       else
1450       {
1451         for ( ; iN < nodes.size(); ++iN )
1452           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1453
1454         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1456
1457         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458                                   uv[0], uv[1], uv[2], uv[3],
1459                                   uv[4], uv[5], uv[6], uv[7] );
1460
1461         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1462         xyz[ 8 ] = p.XYZ();
1463       }
1464
1465       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466                                  uv[8].X(), uv[8].Y() );
1467       myLastCreatedNodes.Append( nCentral );
1468     }
1469
1470     // create 4 triangles
1471
1472     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1473     
1474     helper.SetIsQuadratic  ( nodes.size() > 4 );
1475     helper.SetIsBiQuadratic( nodes.size() == 9 );
1476     if ( helper.GetIsQuadratic() )
1477       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1478
1479     for ( int i = 0; i < 4; ++i )
1480     {
1481       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482                                                nodes[(i+1)%4],
1483                                                nCentral );
1484       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485       myLastCreatedElems.Append( tria );
1486     }
1487   }
1488 }
1489
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose  : Find better diagonal for cutting.
1493 //=======================================================================
1494
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1496                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1497 {
1498   myLastCreatedElems.Clear();
1499   myLastCreatedNodes.Clear();
1500
1501   if (!theCrit.get())
1502     return -1;
1503
1504   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505     return -1;
1506
1507   if( theQuad->NbNodes()==4 ||
1508       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1509
1510     // retrieve element nodes
1511     const SMDS_MeshNode* aNodes [4];
1512     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1513     int i = 0;
1514     //while (itN->more())
1515     while (i<4) {
1516       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1517     }
1518     // compare two sets of possible triangles
1519     double aBadRate1, aBadRate2; // to what extent a set is bad
1520     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1523
1524     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530       return 1; // diagonal 1-3
1531
1532     return 2; // diagonal 2-4
1533   }
1534   return -1;
1535 }
1536
1537 namespace
1538 {
1539   // Methods of splitting volumes into tetra
1540
1541   const int theHexTo5_1[5*4+1] =
1542     {
1543       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1544     };
1545   const int theHexTo5_2[5*4+1] =
1546     {
1547       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1548     };
1549   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1550
1551   const int theHexTo6_1[6*4+1] =
1552     {
1553       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1554     };
1555   const int theHexTo6_2[6*4+1] =
1556     {
1557       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1558     };
1559   const int theHexTo6_3[6*4+1] =
1560     {
1561       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1562     };
1563   const int theHexTo6_4[6*4+1] =
1564     {
1565       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1566     };
1567   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1568
1569   const int thePyraTo2_1[2*4+1] =
1570     {
1571       0, 1, 2, 4,    0, 2, 3, 4,   -1
1572     };
1573   const int thePyraTo2_2[2*4+1] =
1574     {
1575       1, 2, 3, 4,    1, 3, 0, 4,   -1
1576     };
1577   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1578
1579   const int thePentaTo3_1[3*4+1] =
1580     {
1581       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1582     };
1583   const int thePentaTo3_2[3*4+1] =
1584     {
1585       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1586     };
1587   const int thePentaTo3_3[3*4+1] =
1588     {
1589       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1590     };
1591   const int thePentaTo3_4[3*4+1] =
1592     {
1593       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1594     };
1595   const int thePentaTo3_5[3*4+1] =
1596     {
1597       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1598     };
1599   const int thePentaTo3_6[3*4+1] =
1600     {
1601       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1602     };
1603   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1605
1606   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1607   {
1608     int _n1, _n2, _n3;
1609     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1610     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1611     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1612   };
1613   struct TSplitMethod
1614   {
1615     int        _nbTetra;
1616     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1617     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1618     bool       _ownConn;      //!< to delete _connectivity in destructor
1619     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1620
1621     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1622       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1623     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1624     bool hasFacet( const TTriangleFacet& facet ) const
1625     {
1626       const int* tetConn = _connectivity;
1627       for ( ; tetConn[0] >= 0; tetConn += 4 )
1628         if (( facet.contains( tetConn[0] ) +
1629               facet.contains( tetConn[1] ) +
1630               facet.contains( tetConn[2] ) +
1631               facet.contains( tetConn[3] )) == 3 )
1632           return true;
1633       return false;
1634     }
1635   };
1636
1637   //=======================================================================
1638   /*!
1639    * \brief return TSplitMethod for the given element
1640    */
1641   //=======================================================================
1642
1643   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1644   {
1645     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1646
1647     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1648     // an edge and a face barycenter; tertaherdons are based on triangles and
1649     // a volume barycenter
1650     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1651
1652     // Find out how adjacent volumes are split
1653
1654     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1655     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1656     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1657     {
1658       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1659       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1660       if ( nbNodes < 4 ) continue;
1661
1662       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1663       const int* nInd = vol.GetFaceNodesIndices( iF );
1664       if ( nbNodes == 4 )
1665       {
1666         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1667         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1668         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1669         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1670       }
1671       else
1672       {
1673         int iCom = 0; // common node of triangle faces to split into
1674         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1675         {
1676           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1677                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1678                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1679           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1680                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1681                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1682           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1683           {
1684             triaSplits.push_back( t012 );
1685             triaSplits.push_back( t023 );
1686             break;
1687           }
1688         }
1689       }
1690       if ( !triaSplits.empty() )
1691         hasAdjacentSplits = true;
1692     }
1693
1694     // Among variants of split method select one compliant with adjacent volumes
1695
1696     TSplitMethod method;
1697     if ( !vol.Element()->IsPoly() && !is24TetMode )
1698     {
1699       int nbVariants = 2, nbTet = 0;
1700       const int** connVariants = 0;
1701       switch ( vol.Element()->GetEntityType() )
1702       {
1703       case SMDSEntity_Hexa:
1704       case SMDSEntity_Quad_Hexa:
1705       case SMDSEntity_TriQuad_Hexa:
1706         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1707           connVariants = theHexTo5, nbTet = 5;
1708         else
1709           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1710         break;
1711       case SMDSEntity_Pyramid:
1712       case SMDSEntity_Quad_Pyramid:
1713         connVariants = thePyraTo2;  nbTet = 2;
1714         break;
1715       case SMDSEntity_Penta:
1716       case SMDSEntity_Quad_Penta:
1717         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1718         break;
1719       default:
1720         nbVariants = 0;
1721       }
1722       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1723       {
1724         // check method compliancy with adjacent tetras,
1725         // all found splits must be among facets of tetras described by this method
1726         method = TSplitMethod( nbTet, connVariants[variant] );
1727         if ( hasAdjacentSplits && method._nbTetra > 0 )
1728         {
1729           bool facetCreated = true;
1730           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1731           {
1732             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1733             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1734               facetCreated = method.hasFacet( *facet );
1735           }
1736           if ( !facetCreated )
1737             method = TSplitMethod(0); // incompatible method
1738         }
1739       }
1740     }
1741     if ( method._nbTetra < 1 )
1742     {
1743       // No standard method is applicable, use a generic solution:
1744       // each facet of a volume is split into triangles and
1745       // each of triangles and a volume barycenter form a tetrahedron.
1746
1747       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1748
1749       int* connectivity = new int[ maxTetConnSize + 1 ];
1750       method._connectivity = connectivity;
1751       method._ownConn = true;
1752       method._baryNode = !isHex27; // to create central node or not
1753
1754       int connSize = 0;
1755       int baryCenInd = vol.NbNodes() - int( isHex27 );
1756       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1757       {
1758         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1759         const int*   nInd = vol.GetFaceNodesIndices( iF );
1760         // find common node of triangle facets of tetra to create
1761         int iCommon = 0; // index in linear numeration
1762         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1763         if ( !triaSplits.empty() )
1764         {
1765           // by found facets
1766           const TTriangleFacet* facet = &triaSplits.front();
1767           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1768             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1769                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1770               break;
1771         }
1772         else if ( nbNodes > 3 && !is24TetMode )
1773         {
1774           // find the best method of splitting into triangles by aspect ratio
1775           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1776           map< double, int > badness2iCommon;
1777           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1778           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1779           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1780           {
1781             double badness = 0;
1782             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1783             {
1784               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1785                                       nodes[ iQ*((iLast-1)%nbNodes)],
1786                                       nodes[ iQ*((iLast  )%nbNodes)]);
1787               badness += getBadRate( &tria, aspectRatio );
1788             }
1789             badness2iCommon.insert( make_pair( badness, iCommon ));
1790           }
1791           // use iCommon with lowest badness
1792           iCommon = badness2iCommon.begin()->second;
1793         }
1794         if ( iCommon >= nbNodes )
1795           iCommon = 0; // something wrong
1796
1797         // fill connectivity of tetrahedra based on a current face
1798         int nbTet = nbNodes - 2;
1799         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1800         {
1801           int faceBaryCenInd;
1802           if ( isHex27 )
1803           {
1804             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1805             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1806           }
1807           else
1808           {
1809             method._faceBaryNode[ iF ] = 0;
1810             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1811           }
1812           nbTet = nbNodes;
1813           for ( int i = 0; i < nbTet; ++i )
1814           {
1815             int i1 = i, i2 = (i+1) % nbNodes;
1816             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1817             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1818             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1819             connectivity[ connSize++ ] = faceBaryCenInd;
1820             connectivity[ connSize++ ] = baryCenInd;
1821           }
1822         }
1823         else
1824         {
1825           for ( int i = 0; i < nbTet; ++i )
1826           {
1827             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1828             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1829             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1830             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1831             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1832             connectivity[ connSize++ ] = baryCenInd;
1833           }
1834         }
1835         method._nbTetra += nbTet;
1836
1837       } // loop on volume faces
1838
1839       connectivity[ connSize++ ] = -1;
1840
1841     } // end of generic solution
1842
1843     return method;
1844   }
1845   //================================================================================
1846   /*!
1847    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1848    */
1849   //================================================================================
1850
1851   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1852   {
1853     // find the tetrahedron including the three nodes of facet
1854     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1855     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1856     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1857     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1858     while ( volIt1->more() )
1859     {
1860       const SMDS_MeshElement* v = volIt1->next();
1861       SMDSAbs_EntityType type = v->GetEntityType();
1862       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1863         continue;
1864       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1865         continue; // medium node not allowed
1866       const int ind2 = v->GetNodeIndex( n2 );
1867       if ( ind2 < 0 || 3 < ind2 )
1868         continue;
1869       const int ind3 = v->GetNodeIndex( n3 );
1870       if ( ind3 < 0 || 3 < ind3 )
1871         continue;
1872       return true;
1873     }
1874     return false;
1875   }
1876
1877   //=======================================================================
1878   /*!
1879    * \brief A key of a face of volume
1880    */
1881   //=======================================================================
1882
1883   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1884   {
1885     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1886     {
1887       TIDSortedNodeSet sortedNodes;
1888       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1889       int nbNodes = vol.NbFaceNodes( iF );
1890       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1891       for ( int i = 0; i < nbNodes; i += iQ )
1892         sortedNodes.insert( fNodes[i] );
1893       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1894       first.first   = (*(n++))->GetID();
1895       first.second  = (*(n++))->GetID();
1896       second.first  = (*(n++))->GetID();
1897       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1898     }
1899   };
1900 } // namespace
1901
1902 //=======================================================================
1903 //function : SplitVolumesIntoTetra
1904 //purpose  : Split volume elements into tetrahedra.
1905 //=======================================================================
1906
1907 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1908                                               const int                theMethodFlags)
1909 {
1910   // std-like iterator on coordinates of nodes of mesh element
1911   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1912   NXyzIterator xyzEnd;
1913
1914   SMDS_VolumeTool    volTool;
1915   SMESH_MesherHelper helper( *GetMesh());
1916
1917   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1918   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1919
1920   SMESH_SequenceOfElemPtr newNodes, newElems;
1921
1922   // map face of volume to it's baricenrtic node
1923   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1924   double bc[3];
1925
1926   TIDSortedElemSet::const_iterator elem = theElems.begin();
1927   for ( ; elem != theElems.end(); ++elem )
1928   {
1929     if ( (*elem)->GetType() != SMDSAbs_Volume )
1930       continue;
1931     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1932     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1933       continue;
1934
1935     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1936
1937     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1938     if ( splitMethod._nbTetra < 1 ) continue;
1939
1940     // find submesh to add new tetras to
1941     if ( !subMesh || !subMesh->Contains( *elem ))
1942     {
1943       int shapeID = FindShape( *elem );
1944       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1945       subMesh = GetMeshDS()->MeshElements( shapeID );
1946     }
1947     int iQ;
1948     if ( (*elem)->IsQuadratic() )
1949     {
1950       iQ = 2;
1951       // add quadratic links to the helper
1952       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1953       {
1954         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1955         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1956         for ( int iN = 0; iN < nbN; iN += iQ )
1957           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1958       }
1959       helper.SetIsQuadratic( true );
1960     }
1961     else
1962     {
1963       iQ = 1;
1964       helper.SetIsQuadratic( false );
1965     }
1966     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1967     helper.SetElementsOnShape( true );
1968     if ( splitMethod._baryNode )
1969     {
1970       // make a node at barycenter
1971       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1972       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1973       nodes.push_back( gcNode );
1974       newNodes.Append( gcNode );
1975     }
1976     if ( !splitMethod._faceBaryNode.empty() )
1977     {
1978       // make or find baricentric nodes of faces
1979       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1980       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1981       {
1982         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1983           volFace2BaryNode.insert
1984           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1985         if ( !f_n->second )
1986         {
1987           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1988           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1989         }
1990         nodes.push_back( iF_n->second = f_n->second );
1991       }
1992     }
1993
1994     // make tetras
1995     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1996     const int* tetConn = splitMethod._connectivity;
1997     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1998       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1999                                                        nodes[ tetConn[1] ],
2000                                                        nodes[ tetConn[2] ],
2001                                                        nodes[ tetConn[3] ]));
2002
2003     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
2004
2005     // Split faces on sides of the split volume
2006
2007     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2008     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2009     {
2010       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2011       if ( nbNodes < 4 ) continue;
2012
2013       // find an existing face
2014       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2015                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2016       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2017                                                                        /*noMedium=*/false))
2018       {
2019         // make triangles
2020         helper.SetElementsOnShape( false );
2021         vector< const SMDS_MeshElement* > triangles;
2022
2023         // find submesh to add new triangles in
2024         if ( !fSubMesh || !fSubMesh->Contains( face ))
2025         {
2026           int shapeID = FindShape( face );
2027           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2028         }
2029         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2030         if ( iF_n != splitMethod._faceBaryNode.end() )
2031         {
2032           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2033           {
2034             const SMDS_MeshNode* n1 = fNodes[iN];
2035             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2036             const SMDS_MeshNode *n3 = iF_n->second;
2037             if ( !volTool.IsFaceExternal( iF ))
2038               swap( n2, n3 );
2039             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2040
2041             if ( fSubMesh && n3->getshapeId() < 1 )
2042               fSubMesh->AddNode( n3 );
2043           }
2044         }
2045         else
2046         {
2047           // among possible triangles create ones discribed by split method
2048           const int* nInd = volTool.GetFaceNodesIndices( iF );
2049           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2050           int iCom = 0; // common node of triangle faces to split into
2051           list< TTriangleFacet > facets;
2052           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2053           {
2054             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2055                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2056                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2057             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2058                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2059                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2060             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2061             {
2062               facets.push_back( t012 );
2063               facets.push_back( t023 );
2064               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2065                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2066                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2067                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2068               break;
2069             }
2070           }
2071           list< TTriangleFacet >::iterator facet = facets.begin();
2072           for ( ; facet != facets.end(); ++facet )
2073           {
2074             if ( !volTool.IsFaceExternal( iF ))
2075               swap( facet->_n2, facet->_n3 );
2076             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2077                                                  volNodes[ facet->_n2 ],
2078                                                  volNodes[ facet->_n3 ]));
2079           }
2080         }
2081         for ( int i = 0; i < triangles.size(); ++i )
2082         {
2083           if ( !triangles[i] ) continue;
2084           if ( fSubMesh )
2085             fSubMesh->AddElement( triangles[i]);
2086           newElems.Append( triangles[i] );
2087         }
2088         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2089         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2090       }
2091
2092     } // loop on volume faces to split them into triangles
2093
2094     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2095
2096     if ( geomType == SMDSEntity_TriQuad_Hexa )
2097     {
2098       // remove medium nodes that could become free
2099       for ( int i = 20; i < volTool.NbNodes(); ++i )
2100         if ( volNodes[i]->NbInverseElements() == 0 )
2101           GetMeshDS()->RemoveNode( volNodes[i] );
2102     }
2103   } // loop on volumes to split
2104   
2105   myLastCreatedNodes = newNodes;
2106   myLastCreatedElems = newElems;
2107 }
2108
2109 //=======================================================================
2110 //function : AddToSameGroups
2111 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2112 //=======================================================================
2113
2114 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2115                                         const SMDS_MeshElement* elemInGroups,
2116                                         SMESHDS_Mesh *          aMesh)
2117 {
2118   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2119   if (!groups.empty()) {
2120     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2121     for ( ; grIt != groups.end(); grIt++ ) {
2122       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2123       if ( group && group->Contains( elemInGroups ))
2124         group->SMDSGroup().Add( elemToAdd );
2125     }
2126   }
2127 }
2128
2129
2130 //=======================================================================
2131 //function : RemoveElemFromGroups
2132 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2133 //=======================================================================
2134 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2135                                              SMESHDS_Mesh *          aMesh)
2136 {
2137   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2138   if (!groups.empty())
2139   {
2140     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2141     for (; GrIt != groups.end(); GrIt++)
2142     {
2143       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2144       if (!grp || grp->IsEmpty()) continue;
2145       grp->SMDSGroup().Remove(removeelem);
2146     }
2147   }
2148 }
2149
2150 //================================================================================
2151 /*!
2152  * \brief Replace elemToRm by elemToAdd in the all groups
2153  */
2154 //================================================================================
2155
2156 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2157                                             const SMDS_MeshElement* elemToAdd,
2158                                             SMESHDS_Mesh *          aMesh)
2159 {
2160   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2161   if (!groups.empty()) {
2162     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2163     for ( ; grIt != groups.end(); grIt++ ) {
2164       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2165       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2166         group->SMDSGroup().Add( elemToAdd );
2167     }
2168   }
2169 }
2170
2171 //================================================================================
2172 /*!
2173  * \brief Replace elemToRm by elemToAdd in the all groups
2174  */
2175 //================================================================================
2176
2177 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2178                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2179                                             SMESHDS_Mesh *                         aMesh)
2180 {
2181   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2182   if (!groups.empty())
2183   {
2184     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2185     for ( ; grIt != groups.end(); grIt++ ) {
2186       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2187       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2188         for ( int i = 0; i < elemToAdd.size(); ++i )
2189           group->SMDSGroup().Add( elemToAdd[ i ] );
2190     }
2191   }
2192 }
2193
2194 //=======================================================================
2195 //function : QuadToTri
2196 //purpose  : Cut quadrangles into triangles.
2197 //           theCrit is used to select a diagonal to cut
2198 //=======================================================================
2199
2200 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2201                                   const bool         the13Diag)
2202 {
2203   myLastCreatedElems.Clear();
2204   myLastCreatedNodes.Clear();
2205
2206   MESSAGE( "::QuadToTri()" );
2207
2208   SMESHDS_Mesh * aMesh = GetMeshDS();
2209
2210   Handle(Geom_Surface) surface;
2211   SMESH_MesherHelper   helper( *GetMesh() );
2212
2213   TIDSortedElemSet::iterator itElem;
2214   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2215     const SMDS_MeshElement* elem = *itElem;
2216     if ( !elem || elem->GetType() != SMDSAbs_Face )
2217       continue;
2218     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2219     if(!isquad) continue;
2220
2221     if(elem->NbNodes()==4) {
2222       // retrieve element nodes
2223       const SMDS_MeshNode* aNodes [4];
2224       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2225       int i = 0;
2226       while ( itN->more() )
2227         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2228
2229       int aShapeId = FindShape( elem );
2230       const SMDS_MeshElement* newElem1 = 0;
2231       const SMDS_MeshElement* newElem2 = 0;
2232       if ( the13Diag ) {
2233         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2234         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2235       }
2236       else {
2237         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2238         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2239       }
2240       myLastCreatedElems.Append(newElem1);
2241       myLastCreatedElems.Append(newElem2);
2242       // put a new triangle on the same shape and add to the same groups
2243       if ( aShapeId )
2244         {
2245           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2246           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2247         }
2248       AddToSameGroups( newElem1, elem, aMesh );
2249       AddToSameGroups( newElem2, elem, aMesh );
2250       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2251       aMesh->RemoveElement( elem );
2252     }
2253
2254     // Quadratic quadrangle
2255
2256     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2257
2258       // get surface elem is on
2259       int aShapeId = FindShape( elem );
2260       if ( aShapeId != helper.GetSubShapeID() ) {
2261         surface.Nullify();
2262         TopoDS_Shape shape;
2263         if ( aShapeId > 0 )
2264           shape = aMesh->IndexToShape( aShapeId );
2265         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2266           TopoDS_Face face = TopoDS::Face( shape );
2267           surface = BRep_Tool::Surface( face );
2268           if ( !surface.IsNull() )
2269             helper.SetSubShape( shape );
2270         }
2271       }
2272
2273       const SMDS_MeshNode* aNodes [8];
2274       const SMDS_MeshNode* inFaceNode = 0;
2275       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2276       int i = 0;
2277       while ( itN->more() ) {
2278         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2279         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2280              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2281         {
2282           inFaceNode = aNodes[ i-1 ];
2283         }
2284       }
2285
2286       // find middle point for (0,1,2,3)
2287       // and create a node in this point;
2288       gp_XYZ p( 0,0,0 );
2289       if ( surface.IsNull() ) {
2290         for(i=0; i<4; i++)
2291           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2292         p /= 4;
2293       }
2294       else {
2295         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2296         gp_XY uv( 0,0 );
2297         for(i=0; i<4; i++)
2298           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2299         uv /= 4.;
2300         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2301       }
2302       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2303       myLastCreatedNodes.Append(newN);
2304
2305       // create a new element
2306       const SMDS_MeshElement* newElem1 = 0;
2307       const SMDS_MeshElement* newElem2 = 0;
2308       if ( the13Diag ) {
2309         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2310                                   aNodes[6], aNodes[7], newN );
2311         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2312                                   newN,      aNodes[4], aNodes[5] );
2313       }
2314       else {
2315         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2316                                   aNodes[7], aNodes[4], newN );
2317         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2318                                   newN,      aNodes[5], aNodes[6] );
2319       }
2320       myLastCreatedElems.Append(newElem1);
2321       myLastCreatedElems.Append(newElem2);
2322       // put a new triangle on the same shape and add to the same groups
2323       if ( aShapeId )
2324         {
2325           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2326           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2327         }
2328       AddToSameGroups( newElem1, elem, aMesh );
2329       AddToSameGroups( newElem2, elem, aMesh );
2330       aMesh->RemoveElement( elem );
2331     }
2332   }
2333
2334   return true;
2335 }
2336
2337 //=======================================================================
2338 //function : getAngle
2339 //purpose  :
2340 //=======================================================================
2341
2342 double getAngle(const SMDS_MeshElement * tr1,
2343                 const SMDS_MeshElement * tr2,
2344                 const SMDS_MeshNode *    n1,
2345                 const SMDS_MeshNode *    n2)
2346 {
2347   double angle = 2. * M_PI; // bad angle
2348
2349   // get normals
2350   SMESH::Controls::TSequenceOfXYZ P1, P2;
2351   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2352        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2353     return angle;
2354   gp_Vec N1,N2;
2355   if(!tr1->IsQuadratic())
2356     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2357   else
2358     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2359   if ( N1.SquareMagnitude() <= gp::Resolution() )
2360     return angle;
2361   if(!tr2->IsQuadratic())
2362     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2363   else
2364     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2365   if ( N2.SquareMagnitude() <= gp::Resolution() )
2366     return angle;
2367
2368   // find the first diagonal node n1 in the triangles:
2369   // take in account a diagonal link orientation
2370   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2371   for ( int t = 0; t < 2; t++ ) {
2372     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2373     int i = 0, iDiag = -1;
2374     while ( it->more()) {
2375       const SMDS_MeshElement *n = it->next();
2376       if ( n == n1 || n == n2 ) {
2377         if ( iDiag < 0)
2378           iDiag = i;
2379         else {
2380           if ( i - iDiag == 1 )
2381             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2382           else
2383             nFirst[ t ] = n;
2384           break;
2385         }
2386       }
2387       i++;
2388     }
2389   }
2390   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2391     N2.Reverse();
2392
2393   angle = N1.Angle( N2 );
2394   //SCRUTE( angle );
2395   return angle;
2396 }
2397
2398 // =================================================
2399 // class generating a unique ID for a pair of nodes
2400 // and able to return nodes by that ID
2401 // =================================================
2402 class LinkID_Gen {
2403 public:
2404
2405   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2406     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2407   {}
2408
2409   long GetLinkID (const SMDS_MeshNode * n1,
2410                   const SMDS_MeshNode * n2) const
2411   {
2412     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2413   }
2414
2415   bool GetNodes (const long             theLinkID,
2416                  const SMDS_MeshNode* & theNode1,
2417                  const SMDS_MeshNode* & theNode2) const
2418   {
2419     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2420     if ( !theNode1 ) return false;
2421     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2422     if ( !theNode2 ) return false;
2423     return true;
2424   }
2425
2426 private:
2427   LinkID_Gen();
2428   const SMESHDS_Mesh* myMesh;
2429   long                myMaxID;
2430 };
2431
2432
2433 //=======================================================================
2434 //function : TriToQuad
2435 //purpose  : Fuse neighbour triangles into quadrangles.
2436 //           theCrit is used to select a neighbour to fuse with.
2437 //           theMaxAngle is a max angle between element normals at which
2438 //           fusion is still performed.
2439 //=======================================================================
2440
2441 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2442                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2443                                   const double                         theMaxAngle)
2444 {
2445   myLastCreatedElems.Clear();
2446   myLastCreatedNodes.Clear();
2447
2448   MESSAGE( "::TriToQuad()" );
2449
2450   if ( !theCrit.get() )
2451     return false;
2452
2453   SMESHDS_Mesh * aMesh = GetMeshDS();
2454
2455   // Prepare data for algo: build
2456   // 1. map of elements with their linkIDs
2457   // 2. map of linkIDs with their elements
2458
2459   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2460   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2461   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2462   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2463
2464   TIDSortedElemSet::iterator itElem;
2465   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2466   {
2467     const SMDS_MeshElement* elem = *itElem;
2468     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2469     bool IsTria = ( elem->NbCornerNodes()==3 );
2470     if (!IsTria) continue;
2471
2472     // retrieve element nodes
2473     const SMDS_MeshNode* aNodes [4];
2474     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2475     int i = 0;
2476     while ( i < 3 )
2477       aNodes[ i++ ] = itN->next();
2478     aNodes[ 3 ] = aNodes[ 0 ];
2479
2480     // fill maps
2481     for ( i = 0; i < 3; i++ ) {
2482       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2483       // check if elements sharing a link can be fused
2484       itLE = mapLi_listEl.find( link );
2485       if ( itLE != mapLi_listEl.end() ) {
2486         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2487           continue;
2488         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2489         //if ( FindShape( elem ) != FindShape( elem2 ))
2490         //  continue; // do not fuse triangles laying on different shapes
2491         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2492           continue; // avoid making badly shaped quads
2493         (*itLE).second.push_back( elem );
2494       }
2495       else {
2496         mapLi_listEl[ link ].push_back( elem );
2497       }
2498       mapEl_setLi [ elem ].insert( link );
2499     }
2500   }
2501   // Clean the maps from the links shared by a sole element, ie
2502   // links to which only one element is bound in mapLi_listEl
2503
2504   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2505     int nbElems = (*itLE).second.size();
2506     if ( nbElems < 2  ) {
2507       const SMDS_MeshElement* elem = (*itLE).second.front();
2508       SMESH_TLink link = (*itLE).first;
2509       mapEl_setLi[ elem ].erase( link );
2510       if ( mapEl_setLi[ elem ].empty() )
2511         mapEl_setLi.erase( elem );
2512     }
2513   }
2514
2515   // Algo: fuse triangles into quadrangles
2516
2517   while ( ! mapEl_setLi.empty() ) {
2518     // Look for the start element:
2519     // the element having the least nb of shared links
2520     const SMDS_MeshElement* startElem = 0;
2521     int minNbLinks = 4;
2522     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2523       int nbLinks = (*itEL).second.size();
2524       if ( nbLinks < minNbLinks ) {
2525         startElem = (*itEL).first;
2526         minNbLinks = nbLinks;
2527         if ( minNbLinks == 1 )
2528           break;
2529       }
2530     }
2531
2532     // search elements to fuse starting from startElem or links of elements
2533     // fused earlyer - startLinks
2534     list< SMESH_TLink > startLinks;
2535     while ( startElem || !startLinks.empty() ) {
2536       while ( !startElem && !startLinks.empty() ) {
2537         // Get an element to start, by a link
2538         SMESH_TLink linkId = startLinks.front();
2539         startLinks.pop_front();
2540         itLE = mapLi_listEl.find( linkId );
2541         if ( itLE != mapLi_listEl.end() ) {
2542           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2543           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2544           for ( ; itE != listElem.end() ; itE++ )
2545             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2546               startElem = (*itE);
2547           mapLi_listEl.erase( itLE );
2548         }
2549       }
2550
2551       if ( startElem ) {
2552         // Get candidates to be fused
2553         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2554         const SMESH_TLink *link12, *link13;
2555         startElem = 0;
2556         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2557         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2558         ASSERT( !setLi.empty() );
2559         set< SMESH_TLink >::iterator itLi;
2560         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2561         {
2562           const SMESH_TLink & link = (*itLi);
2563           itLE = mapLi_listEl.find( link );
2564           if ( itLE == mapLi_listEl.end() )
2565             continue;
2566
2567           const SMDS_MeshElement* elem = (*itLE).second.front();
2568           if ( elem == tr1 )
2569             elem = (*itLE).second.back();
2570           mapLi_listEl.erase( itLE );
2571           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2572             continue;
2573           if ( tr2 ) {
2574             tr3 = elem;
2575             link13 = &link;
2576           }
2577           else {
2578             tr2 = elem;
2579             link12 = &link;
2580           }
2581
2582           // add other links of elem to list of links to re-start from
2583           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2584           set< SMESH_TLink >::iterator it;
2585           for ( it = links.begin(); it != links.end(); it++ ) {
2586             const SMESH_TLink& link2 = (*it);
2587             if ( link2 != link )
2588               startLinks.push_back( link2 );
2589           }
2590         }
2591
2592         // Get nodes of possible quadrangles
2593         const SMDS_MeshNode *n12 [4], *n13 [4];
2594         bool Ok12 = false, Ok13 = false;
2595         const SMDS_MeshNode *linkNode1, *linkNode2;
2596         if(tr2) {
2597           linkNode1 = link12->first;
2598           linkNode2 = link12->second;
2599           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2600             Ok12 = true;
2601         }
2602         if(tr3) {
2603           linkNode1 = link13->first;
2604           linkNode2 = link13->second;
2605           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2606             Ok13 = true;
2607         }
2608
2609         // Choose a pair to fuse
2610         if ( Ok12 && Ok13 ) {
2611           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2612           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2613           double aBadRate12 = getBadRate( &quad12, theCrit );
2614           double aBadRate13 = getBadRate( &quad13, theCrit );
2615           if (  aBadRate13 < aBadRate12 )
2616             Ok12 = false;
2617           else
2618             Ok13 = false;
2619         }
2620
2621         // Make quadrangles
2622         // and remove fused elems and remove links from the maps
2623         mapEl_setLi.erase( tr1 );
2624         if ( Ok12 )
2625         {
2626           mapEl_setLi.erase( tr2 );
2627           mapLi_listEl.erase( *link12 );
2628           if ( tr1->NbNodes() == 3 )
2629           {
2630             const SMDS_MeshElement* newElem = 0;
2631             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2632             myLastCreatedElems.Append(newElem);
2633             AddToSameGroups( newElem, tr1, aMesh );
2634             int aShapeId = tr1->getshapeId();
2635             if ( aShapeId )
2636               aMesh->SetMeshElementOnShape( newElem, aShapeId );
2637             aMesh->RemoveElement( tr1 );
2638             aMesh->RemoveElement( tr2 );
2639           }
2640           else {
2641             vector< const SMDS_MeshNode* > N1;
2642             vector< const SMDS_MeshNode* > N2;
2643             getNodesFromTwoTria(tr1,tr2,N1,N2);
2644             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
2645             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2646             // i.e. first nodes from both arrays form a new diagonal
2647             const SMDS_MeshNode* aNodes[8];
2648             aNodes[0] = N1[0];
2649             aNodes[1] = N1[1];
2650             aNodes[2] = N2[0];
2651             aNodes[3] = N2[1];
2652             aNodes[4] = N1[3];
2653             aNodes[5] = N2[5];
2654             aNodes[6] = N2[3];
2655             aNodes[7] = N1[5];
2656             const SMDS_MeshElement* newElem = 0;
2657             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2658               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2659                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2660             else
2661               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2662                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2663             myLastCreatedElems.Append(newElem);
2664             AddToSameGroups( newElem, tr1, aMesh );
2665             int aShapeId = tr1->getshapeId();
2666             if ( aShapeId )
2667               aMesh->SetMeshElementOnShape( newElem, aShapeId );
2668             aMesh->RemoveElement( tr1 );
2669             aMesh->RemoveElement( tr2 );
2670             // remove middle node (9)
2671             if ( N1[4]->NbInverseElements() == 0 )
2672               aMesh->RemoveNode( N1[4] );
2673             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2674               aMesh->RemoveNode( N1[6] );
2675             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2676               aMesh->RemoveNode( N2[6] );
2677           }
2678         }
2679         else if ( Ok13 )
2680         {
2681           mapEl_setLi.erase( tr3 );
2682           mapLi_listEl.erase( *link13 );
2683           if ( tr1->NbNodes() == 3 ) {
2684             const SMDS_MeshElement* newElem = 0;
2685             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2686             myLastCreatedElems.Append(newElem);
2687             AddToSameGroups( newElem, tr1, aMesh );
2688             int aShapeId = tr1->getshapeId();
2689             if ( aShapeId )
2690               aMesh->SetMeshElementOnShape( newElem, aShapeId );
2691             aMesh->RemoveElement( tr1 );
2692             aMesh->RemoveElement( tr3 );
2693           }
2694           else {
2695             vector< const SMDS_MeshNode* > N1;
2696             vector< const SMDS_MeshNode* > N2;
2697             getNodesFromTwoTria(tr1,tr3,N1,N2);
2698             // now we receive following N1 and N2 (using numeration as above image)
2699             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2700             // i.e. first nodes from both arrays form a new diagonal
2701             const SMDS_MeshNode* aNodes[8];
2702             aNodes[0] = N1[0];
2703             aNodes[1] = N1[1];
2704             aNodes[2] = N2[0];
2705             aNodes[3] = N2[1];
2706             aNodes[4] = N1[3];
2707             aNodes[5] = N2[5];
2708             aNodes[6] = N2[3];
2709             aNodes[7] = N1[5];
2710             const SMDS_MeshElement* newElem = 0;
2711             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2712               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2713                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2714             else
2715               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2716                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2717             myLastCreatedElems.Append(newElem);
2718             AddToSameGroups( newElem, tr1, aMesh );
2719             int aShapeId = tr1->getshapeId();
2720             if ( aShapeId )
2721               aMesh->SetMeshElementOnShape( newElem, aShapeId );
2722             aMesh->RemoveElement( tr1 );
2723             aMesh->RemoveElement( tr3 );
2724             // remove middle node (9)
2725             if ( N1[4]->NbInverseElements() == 0 )
2726               aMesh->RemoveNode( N1[4] );
2727             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2728               aMesh->RemoveNode( N1[6] );
2729             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2730               aMesh->RemoveNode( N2[6] );
2731           }
2732         }
2733
2734         // Next element to fuse: the rejected one
2735         if ( tr3 )
2736           startElem = Ok12 ? tr3 : tr2;
2737
2738       } // if ( startElem )
2739     } // while ( startElem || !startLinks.empty() )
2740   } // while ( ! mapEl_setLi.empty() )
2741
2742   return true;
2743 }
2744
2745
2746 /*#define DUMPSO(txt) \
2747 //  cout << txt << endl;
2748 //=============================================================================
2749 //
2750 //
2751 //
2752 //=============================================================================
2753 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2754 {
2755 if ( i1 == i2 )
2756 return;
2757 int tmp = idNodes[ i1 ];
2758 idNodes[ i1 ] = idNodes[ i2 ];
2759 idNodes[ i2 ] = tmp;
2760 gp_Pnt Ptmp = P[ i1 ];
2761 P[ i1 ] = P[ i2 ];
2762 P[ i2 ] = Ptmp;
2763 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2764 }
2765
2766 //=======================================================================
2767 //function : SortQuadNodes
2768 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2769 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2770 //           1 or 2 else 0.
2771 //=======================================================================
2772
2773 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2774 int               idNodes[] )
2775 {
2776   gp_Pnt P[4];
2777   int i;
2778   for ( i = 0; i < 4; i++ ) {
2779     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2780     if ( !n ) return 0;
2781     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2782   }
2783
2784   gp_Vec V1(P[0], P[1]);
2785   gp_Vec V2(P[0], P[2]);
2786   gp_Vec V3(P[0], P[3]);
2787
2788   gp_Vec Cross1 = V1 ^ V2;
2789   gp_Vec Cross2 = V2 ^ V3;
2790
2791   i = 0;
2792   if (Cross1.Dot(Cross2) < 0)
2793   {
2794     Cross1 = V2 ^ V1;
2795     Cross2 = V1 ^ V3;
2796
2797     if (Cross1.Dot(Cross2) < 0)
2798       i = 2;
2799     else
2800       i = 1;
2801     swap ( i, i + 1, idNodes, P );
2802
2803     //     for ( int ii = 0; ii < 4; ii++ ) {
2804     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2805     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2806     //     }
2807   }
2808   return i;
2809 }
2810
2811 //=======================================================================
2812 //function : SortHexaNodes
2813 //purpose  : Set 8 nodes of a hexahedron in a good order.
2814 //           Return success status
2815 //=======================================================================
2816
2817 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2818                                       int               idNodes[] )
2819 {
2820   gp_Pnt P[8];
2821   int i;
2822   DUMPSO( "INPUT: ========================================");
2823   for ( i = 0; i < 8; i++ ) {
2824     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2825     if ( !n ) return false;
2826     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2827     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2828   }
2829   DUMPSO( "========================================");
2830
2831
2832   set<int> faceNodes;  // ids of bottom face nodes, to be found
2833   set<int> checkedId1; // ids of tried 2-nd nodes
2834   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2835   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2836   int iMin, iLoop1 = 0;
2837
2838   // Loop to try the 2-nd nodes
2839
2840   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2841   {
2842     // Find not checked 2-nd node
2843     for ( i = 1; i < 8; i++ )
2844       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2845         int id1 = idNodes[i];
2846         swap ( 1, i, idNodes, P );
2847         checkedId1.insert ( id1 );
2848         break;
2849       }
2850
2851     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2852     // ie that all but meybe one (id3 which is on the same face) nodes
2853     // lay on the same side from the triangle plane.
2854
2855     bool manyInPlane = false; // more than 4 nodes lay in plane
2856     int iLoop2 = 0;
2857     while ( ++iLoop2 < 6 ) {
2858
2859       // get 1-2-3 plane coeffs
2860       Standard_Real A, B, C, D;
2861       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2862       if ( N.SquareMagnitude() > gp::Resolution() )
2863       {
2864         gp_Pln pln ( P[0], N );
2865         pln.Coefficients( A, B, C, D );
2866
2867         // find the node (iMin) closest to pln
2868         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2869         set<int> idInPln;
2870         for ( i = 3; i < 8; i++ ) {
2871           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2872           if ( fabs( dist[i] ) < minDist ) {
2873             minDist = fabs( dist[i] );
2874             iMin = i;
2875           }
2876           if ( fabs( dist[i] ) <= tol )
2877             idInPln.insert( idNodes[i] );
2878         }
2879
2880         // there should not be more than 4 nodes in bottom plane
2881         if ( idInPln.size() > 1 )
2882         {
2883           DUMPSO( "### idInPln.size() = " << idInPln.size());
2884           // idInPlane does not contain the first 3 nodes
2885           if ( manyInPlane || idInPln.size() == 5)
2886             return false; // all nodes in one plane
2887           manyInPlane = true;
2888
2889           // set the 1-st node to be not in plane
2890           for ( i = 3; i < 8; i++ ) {
2891             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2892               DUMPSO( "### Reset 0-th node");
2893               swap( 0, i, idNodes, P );
2894               break;
2895             }
2896           }
2897
2898           // reset to re-check second nodes
2899           leastDist = DBL_MAX;
2900           faceNodes.clear();
2901           checkedId1.clear();
2902           iLoop1 = 0;
2903           break; // from iLoop2;
2904         }
2905
2906         // check that the other 4 nodes are on the same side
2907         bool sameSide = true;
2908         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2909         for ( i = 3; sameSide && i < 8; i++ ) {
2910           if ( i != iMin )
2911             sameSide = ( isNeg == dist[i] <= 0.);
2912         }
2913
2914         // keep best solution
2915         if ( sameSide && minDist < leastDist ) {
2916           leastDist = minDist;
2917           faceNodes.clear();
2918           faceNodes.insert( idNodes[ 1 ] );
2919           faceNodes.insert( idNodes[ 2 ] );
2920           faceNodes.insert( idNodes[ iMin ] );
2921           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2922                   << " leastDist = " << leastDist);
2923           if ( leastDist <= DBL_MIN )
2924             break;
2925         }
2926       }
2927
2928       // set next 3-d node to check
2929       int iNext = 2 + iLoop2;
2930       if ( iNext < 8 ) {
2931         DUMPSO( "Try 2-nd");
2932         swap ( 2, iNext, idNodes, P );
2933       }
2934     } // while ( iLoop2 < 6 )
2935   } // iLoop1
2936
2937   if ( faceNodes.empty() ) return false;
2938
2939   // Put the faceNodes in proper places
2940   for ( i = 4; i < 8; i++ ) {
2941     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2942       // find a place to put
2943       int iTo = 1;
2944       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2945         iTo++;
2946       DUMPSO( "Set faceNodes");
2947       swap ( iTo, i, idNodes, P );
2948     }
2949   }
2950
2951
2952   // Set nodes of the found bottom face in good order
2953   DUMPSO( " Found bottom face: ");
2954   i = SortQuadNodes( theMesh, idNodes );
2955   if ( i ) {
2956     gp_Pnt Ptmp = P[ i ];
2957     P[ i ] = P[ i+1 ];
2958     P[ i+1 ] = Ptmp;
2959   }
2960   //   else
2961   //     for ( int ii = 0; ii < 4; ii++ ) {
2962   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2963   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2964   //    }
2965
2966   // Gravity center of the top and bottom faces
2967   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2968   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2969
2970   // Get direction from the bottom to the top face
2971   gp_Vec upDir ( aGCb, aGCt );
2972   Standard_Real upDirSize = upDir.Magnitude();
2973   if ( upDirSize <= gp::Resolution() ) return false;
2974   upDir / upDirSize;
2975
2976   // Assure that the bottom face normal points up
2977   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2978   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2979   if ( Nb.Dot( upDir ) < 0 ) {
2980     DUMPSO( "Reverse bottom face");
2981     swap( 1, 3, idNodes, P );
2982   }
2983
2984   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2985   Standard_Real minDist = DBL_MAX;
2986   for ( i = 4; i < 8; i++ ) {
2987     // projection of P[i] to the plane defined by P[0] and upDir
2988     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2989     Standard_Real sqDist = P[0].SquareDistance( Pp );
2990     if ( sqDist < minDist ) {
2991       minDist = sqDist;
2992       iMin = i;
2993     }
2994   }
2995   DUMPSO( "Set 4-th");
2996   swap ( 4, iMin, idNodes, P );
2997
2998   // Set nodes of the top face in good order
2999   DUMPSO( "Sort top face");
3000   i = SortQuadNodes( theMesh, &idNodes[4] );
3001   if ( i ) {
3002     i += 4;
3003     gp_Pnt Ptmp = P[ i ];
3004     P[ i ] = P[ i+1 ];
3005     P[ i+1 ] = Ptmp;
3006   }
3007
3008   // Assure that direction of the top face normal is from the bottom face
3009   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3010   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3011   if ( Nt.Dot( upDir ) < 0 ) {
3012     DUMPSO( "Reverse top face");
3013     swap( 5, 7, idNodes, P );
3014   }
3015
3016   //   DUMPSO( "OUTPUT: ========================================");
3017   //   for ( i = 0; i < 8; i++ ) {
3018   //     float *p = ugrid->GetPoint(idNodes[i]);
3019   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3020   //   }
3021
3022   return true;
3023 }*/
3024
3025 //================================================================================
3026 /*!
3027  * \brief Return nodes linked to the given one
3028  * \param theNode - the node
3029  * \param linkedNodes - the found nodes
3030  * \param type - the type of elements to check
3031  *
3032  * Medium nodes are ignored
3033  */
3034 //================================================================================
3035
3036 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3037                                        TIDSortedElemSet &   linkedNodes,
3038                                        SMDSAbs_ElementType  type )
3039 {
3040   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3041   while ( elemIt->more() )
3042   {
3043     const SMDS_MeshElement* elem = elemIt->next();
3044     if(elem->GetType() == SMDSAbs_0DElement)
3045       continue;
3046
3047     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3048     if ( elem->GetType() == SMDSAbs_Volume )
3049     {
3050       SMDS_VolumeTool vol( elem );
3051       while ( nodeIt->more() ) {
3052         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3053         if ( theNode != n && vol.IsLinked( theNode, n ))
3054           linkedNodes.insert( n );
3055       }
3056     }
3057     else
3058     {
3059       for ( int i = 0; nodeIt->more(); ++i ) {
3060         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3061         if ( n == theNode ) {
3062           int iBefore = i - 1;
3063           int iAfter  = i + 1;
3064           if ( elem->IsQuadratic() ) {
3065             int nb = elem->NbNodes() / 2;
3066             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3067             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3068           }
3069           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3070           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3071         }
3072       }
3073     }
3074   }
3075 }
3076
3077 //=======================================================================
3078 //function : laplacianSmooth
3079 //purpose  : pulls theNode toward the center of surrounding nodes directly
3080 //           connected to that node along an element edge
3081 //=======================================================================
3082
3083 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3084                      const Handle(Geom_Surface)&          theSurface,
3085                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3086 {
3087   // find surrounding nodes
3088
3089   TIDSortedElemSet nodeSet;
3090   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3091
3092   // compute new coodrs
3093
3094   double coord[] = { 0., 0., 0. };
3095   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3096   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3097     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3098     if ( theSurface.IsNull() ) { // smooth in 3D
3099       coord[0] += node->X();
3100       coord[1] += node->Y();
3101       coord[2] += node->Z();
3102     }
3103     else { // smooth in 2D
3104       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3105       gp_XY* uv = theUVMap[ node ];
3106       coord[0] += uv->X();
3107       coord[1] += uv->Y();
3108     }
3109   }
3110   int nbNodes = nodeSet.size();
3111   if ( !nbNodes )
3112     return;
3113   coord[0] /= nbNodes;
3114   coord[1] /= nbNodes;
3115
3116   if ( !theSurface.IsNull() ) {
3117     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3118     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3119     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3120     coord[0] = p3d.X();
3121     coord[1] = p3d.Y();
3122     coord[2] = p3d.Z();
3123   }
3124   else
3125     coord[2] /= nbNodes;
3126
3127   // move node
3128
3129   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3130 }
3131
3132 //=======================================================================
3133 //function : centroidalSmooth
3134 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3135 //           surrounding elements
3136 //=======================================================================
3137
3138 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3139                       const Handle(Geom_Surface)&          theSurface,
3140                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3141 {
3142   gp_XYZ aNewXYZ(0.,0.,0.);
3143   SMESH::Controls::Area anAreaFunc;
3144   double totalArea = 0.;
3145   int nbElems = 0;
3146
3147   // compute new XYZ
3148
3149   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3150   while ( elemIt->more() )
3151   {
3152     const SMDS_MeshElement* elem = elemIt->next();
3153     nbElems++;
3154
3155     gp_XYZ elemCenter(0.,0.,0.);
3156     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3157     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3158     int nn = elem->NbNodes();
3159     if(elem->IsQuadratic()) nn = nn/2;
3160     int i=0;
3161     //while ( itN->more() ) {
3162     while ( i<nn ) {
3163       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3164       i++;
3165       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3166       aNodePoints.push_back( aP );
3167       if ( !theSurface.IsNull() ) { // smooth in 2D
3168         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3169         gp_XY* uv = theUVMap[ aNode ];
3170         aP.SetCoord( uv->X(), uv->Y(), 0. );
3171       }
3172       elemCenter += aP;
3173     }
3174     double elemArea = anAreaFunc.GetValue( aNodePoints );
3175     totalArea += elemArea;
3176     elemCenter /= nn;
3177     aNewXYZ += elemCenter * elemArea;
3178   }
3179   aNewXYZ /= totalArea;
3180   if ( !theSurface.IsNull() ) {
3181     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3182     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3183   }
3184
3185   // move node
3186
3187   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3188 }
3189
3190 //=======================================================================
3191 //function : getClosestUV
3192 //purpose  : return UV of closest projection
3193 //=======================================================================
3194
3195 static bool getClosestUV (Extrema_GenExtPS& projector,
3196                           const gp_Pnt&     point,
3197                           gp_XY &           result)
3198 {
3199   projector.Perform( point );
3200   if ( projector.IsDone() ) {
3201     double u, v, minVal = DBL_MAX;
3202     for ( int i = projector.NbExt(); i > 0; i-- )
3203 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3204       if ( projector.SquareDistance( i ) < minVal ) {
3205         minVal = projector.SquareDistance( i );
3206 #else
3207       if ( projector.Value( i ) < minVal ) {
3208         minVal = projector.Value( i );
3209 #endif
3210         projector.Point( i ).Parameter( u, v );
3211       }
3212     result.SetCoord( u, v );
3213     return true;
3214   }
3215   return false;
3216 }
3217
3218 //=======================================================================
3219 //function : Smooth
3220 //purpose  : Smooth theElements during theNbIterations or until a worst
3221 //           element has aspect ratio <= theTgtAspectRatio.
3222 //           Aspect Ratio varies in range [1.0, inf].
3223 //           If theElements is empty, the whole mesh is smoothed.
3224 //           theFixedNodes contains additionally fixed nodes. Nodes built
3225 //           on edges and boundary nodes are always fixed.
3226 //=======================================================================
3227
3228 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3229                                set<const SMDS_MeshNode*> & theFixedNodes,
3230                                const SmoothMethod          theSmoothMethod,
3231                                const int                   theNbIterations,
3232                                double                      theTgtAspectRatio,
3233                                const bool                  the2D)
3234 {
3235   myLastCreatedElems.Clear();
3236   myLastCreatedNodes.Clear();
3237
3238   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3239
3240   if ( theTgtAspectRatio < 1.0 )
3241     theTgtAspectRatio = 1.0;
3242
3243   const double disttol = 1.e-16;
3244
3245   SMESH::Controls::AspectRatio aQualityFunc;
3246
3247   SMESHDS_Mesh* aMesh = GetMeshDS();
3248
3249   if ( theElems.empty() ) {
3250     // add all faces to theElems
3251     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3252     while ( fIt->more() ) {
3253       const SMDS_MeshElement* face = fIt->next();
3254       theElems.insert( theElems.end(), face );
3255     }
3256   }
3257   // get all face ids theElems are on
3258   set< int > faceIdSet;
3259   TIDSortedElemSet::iterator itElem;
3260   if ( the2D )
3261     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3262       int fId = FindShape( *itElem );
3263       // check that corresponding submesh exists and a shape is face
3264       if (fId &&
3265           faceIdSet.find( fId ) == faceIdSet.end() &&
3266           aMesh->MeshElements( fId )) {
3267         TopoDS_Shape F = aMesh->IndexToShape( fId );
3268         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3269           faceIdSet.insert( fId );
3270       }
3271     }
3272   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3273
3274   // ===============================================
3275   // smooth elements on each TopoDS_Face separately
3276   // ===============================================
3277
3278   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3279   for ( ; fId != faceIdSet.rend(); ++fId ) {
3280     // get face surface and submesh
3281     Handle(Geom_Surface) surface;
3282     SMESHDS_SubMesh* faceSubMesh = 0;
3283     TopoDS_Face face;
3284     double fToler2 = 0, f,l;
3285     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3286     bool isUPeriodic = false, isVPeriodic = false;
3287     if ( *fId ) {
3288       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3289       surface = BRep_Tool::Surface( face );
3290       faceSubMesh = aMesh->MeshElements( *fId );
3291       fToler2 = BRep_Tool::Tolerance( face );
3292       fToler2 *= fToler2 * 10.;
3293       isUPeriodic = surface->IsUPeriodic();
3294       if ( isUPeriodic )
3295         surface->UPeriod();
3296       isVPeriodic = surface->IsVPeriodic();
3297       if ( isVPeriodic )
3298         surface->VPeriod();
3299       surface->Bounds( u1, u2, v1, v2 );
3300     }
3301     // ---------------------------------------------------------
3302     // for elements on a face, find movable and fixed nodes and
3303     // compute UV for them
3304     // ---------------------------------------------------------
3305     bool checkBoundaryNodes = false;
3306     bool isQuadratic = false;
3307     set<const SMDS_MeshNode*> setMovableNodes;
3308     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3309     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3310     list< const SMDS_MeshElement* > elemsOnFace;
3311
3312     Extrema_GenExtPS projector;
3313     GeomAdaptor_Surface surfAdaptor;
3314     if ( !surface.IsNull() ) {
3315       surfAdaptor.Load( surface );
3316       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3317     }
3318     int nbElemOnFace = 0;
3319     itElem = theElems.begin();
3320     // loop on not yet smoothed elements: look for elems on a face
3321     while ( itElem != theElems.end() ) {
3322       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3323         break; // all elements found
3324
3325       const SMDS_MeshElement* elem = *itElem;
3326       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3327            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3328         ++itElem;
3329         continue;
3330       }
3331       elemsOnFace.push_back( elem );
3332       theElems.erase( itElem++ );
3333       nbElemOnFace++;
3334
3335       if ( !isQuadratic )
3336         isQuadratic = elem->IsQuadratic();
3337
3338       // get movable nodes of elem
3339       const SMDS_MeshNode* node;
3340       SMDS_TypeOfPosition posType;
3341       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3342       int nn = 0, nbn =  elem->NbNodes();
3343       if(elem->IsQuadratic())
3344         nbn = nbn/2;
3345       while ( nn++ < nbn ) {
3346         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3347         const SMDS_PositionPtr& pos = node->GetPosition();
3348         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3349         if (posType != SMDS_TOP_EDGE &&
3350             posType != SMDS_TOP_VERTEX &&
3351             theFixedNodes.find( node ) == theFixedNodes.end())
3352         {
3353           // check if all faces around the node are on faceSubMesh
3354           // because a node on edge may be bound to face
3355           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3356           bool all = true;
3357           if ( faceSubMesh ) {
3358             while ( eIt->more() && all ) {
3359               const SMDS_MeshElement* e = eIt->next();
3360               all = faceSubMesh->Contains( e );
3361             }
3362           }
3363           if ( all )
3364             setMovableNodes.insert( node );
3365           else
3366             checkBoundaryNodes = true;
3367         }
3368         if ( posType == SMDS_TOP_3DSPACE )
3369           checkBoundaryNodes = true;
3370       }
3371
3372       if ( surface.IsNull() )
3373         continue;
3374
3375       // get nodes to check UV
3376       list< const SMDS_MeshNode* > uvCheckNodes;
3377       itN = elem->nodesIterator();
3378       nn = 0; nbn =  elem->NbNodes();
3379       if(elem->IsQuadratic())
3380         nbn = nbn/2;
3381       while ( nn++ < nbn ) {
3382         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3383         if ( uvMap.find( node ) == uvMap.end() )
3384           uvCheckNodes.push_back( node );
3385         // add nodes of elems sharing node
3386         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3387         //         while ( eIt->more() ) {
3388         //           const SMDS_MeshElement* e = eIt->next();
3389         //           if ( e != elem ) {
3390         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3391         //             while ( nIt->more() ) {
3392         //               const SMDS_MeshNode* n =
3393         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3394         //               if ( uvMap.find( n ) == uvMap.end() )
3395         //                 uvCheckNodes.push_back( n );
3396         //             }
3397         //           }
3398         //         }
3399       }
3400       // check UV on face
3401       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3402       for ( ; n != uvCheckNodes.end(); ++n ) {
3403         node = *n;
3404         gp_XY uv( 0, 0 );
3405         const SMDS_PositionPtr& pos = node->GetPosition();
3406         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3407         // get existing UV
3408         switch ( posType ) {
3409         case SMDS_TOP_FACE: {
3410           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3411           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3412           break;
3413         }
3414         case SMDS_TOP_EDGE: {
3415           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3416           Handle(Geom2d_Curve) pcurve;
3417           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3418             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3419           if ( !pcurve.IsNull() ) {
3420             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3421             uv = pcurve->Value( u ).XY();
3422           }
3423           break;
3424         }
3425         case SMDS_TOP_VERTEX: {
3426           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3427           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3428             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3429           break;
3430         }
3431         default:;
3432         }
3433         // check existing UV
3434         bool project = true;
3435         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3436         double dist1 = DBL_MAX, dist2 = 0;
3437         if ( posType != SMDS_TOP_3DSPACE ) {
3438           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3439           project = dist1 > fToler2;
3440         }
3441         if ( project ) { // compute new UV
3442           gp_XY newUV;
3443           if ( !getClosestUV( projector, pNode, newUV )) {
3444             MESSAGE("Node Projection Failed " << node);
3445           }
3446           else {
3447             if ( isUPeriodic )
3448               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3449             if ( isVPeriodic )
3450               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3451             // check new UV
3452             if ( posType != SMDS_TOP_3DSPACE )
3453               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3454             if ( dist2 < dist1 )
3455               uv = newUV;
3456           }
3457         }
3458         // store UV in the map
3459         listUV.push_back( uv );
3460         uvMap.insert( make_pair( node, &listUV.back() ));
3461       }
3462     } // loop on not yet smoothed elements
3463
3464     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3465       checkBoundaryNodes = true;
3466
3467     // fix nodes on mesh boundary
3468
3469     if ( checkBoundaryNodes ) {
3470       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3471       map< SMESH_TLink, int >::iterator link_nb;
3472       // put all elements links to linkNbMap
3473       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3474       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3475         const SMDS_MeshElement* elem = (*elemIt);
3476         int nbn =  elem->NbCornerNodes();
3477         // loop on elem links: insert them in linkNbMap
3478         for ( int iN = 0; iN < nbn; ++iN ) {
3479           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3480           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3481           SMESH_TLink link( n1, n2 );
3482           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3483           link_nb->second++;
3484         }
3485       }
3486       // remove nodes that are in links encountered only once from setMovableNodes
3487       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3488         if ( link_nb->second == 1 ) {
3489           setMovableNodes.erase( link_nb->first.node1() );
3490           setMovableNodes.erase( link_nb->first.node2() );
3491         }
3492       }
3493     }
3494
3495     // -----------------------------------------------------
3496     // for nodes on seam edge, compute one more UV ( uvMap2 );
3497     // find movable nodes linked to nodes on seam and which
3498     // are to be smoothed using the second UV ( uvMap2 )
3499     // -----------------------------------------------------
3500
3501     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3502     if ( !surface.IsNull() ) {
3503       TopExp_Explorer eExp( face, TopAbs_EDGE );
3504       for ( ; eExp.More(); eExp.Next() ) {
3505         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3506         if ( !BRep_Tool::IsClosed( edge, face ))
3507           continue;
3508         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3509         if ( !sm ) continue;
3510         // find out which parameter varies for a node on seam
3511         double f,l;
3512         gp_Pnt2d uv1, uv2;
3513         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3514         if ( pcurve.IsNull() ) continue;
3515         uv1 = pcurve->Value( f );
3516         edge.Reverse();
3517         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3518         if ( pcurve.IsNull() ) continue;
3519         uv2 = pcurve->Value( f );
3520         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3521         // assure uv1 < uv2
3522         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3523           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3524         }
3525         // get nodes on seam and its vertices
3526         list< const SMDS_MeshNode* > seamNodes;
3527         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3528         while ( nSeamIt->more() ) {
3529           const SMDS_MeshNode* node = nSeamIt->next();
3530           if ( !isQuadratic || !IsMedium( node ))
3531             seamNodes.push_back( node );
3532         }
3533         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3534         for ( ; vExp.More(); vExp.Next() ) {
3535           sm = aMesh->MeshElements( vExp.Current() );
3536           if ( sm ) {
3537             nSeamIt = sm->GetNodes();
3538             while ( nSeamIt->more() )
3539               seamNodes.push_back( nSeamIt->next() );
3540           }
3541         }
3542         // loop on nodes on seam
3543         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3544         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3545           const SMDS_MeshNode* nSeam = *noSeIt;
3546           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3547           if ( n_uv == uvMap.end() )
3548             continue;
3549           // set the first UV
3550           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3551           // set the second UV
3552           listUV.push_back( *n_uv->second );
3553           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3554           if ( uvMap2.empty() )
3555             uvMap2 = uvMap; // copy the uvMap contents
3556           uvMap2[ nSeam ] = &listUV.back();
3557
3558           // collect movable nodes linked to ones on seam in nodesNearSeam
3559           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3560           while ( eIt->more() ) {
3561             const SMDS_MeshElement* e = eIt->next();
3562             int nbUseMap1 = 0, nbUseMap2 = 0;
3563             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3564             int nn = 0, nbn =  e->NbNodes();
3565             if(e->IsQuadratic()) nbn = nbn/2;
3566             while ( nn++ < nbn )
3567             {
3568               const SMDS_MeshNode* n =
3569                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3570               if (n == nSeam ||
3571                   setMovableNodes.find( n ) == setMovableNodes.end() )
3572                 continue;
3573               // add only nodes being closer to uv2 than to uv1
3574               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3575                            0.5 * ( n->Y() + nSeam->Y() ),
3576                            0.5 * ( n->Z() + nSeam->Z() ));
3577               gp_XY uv;
3578               getClosestUV( projector, pMid, uv );
3579               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3580                 nodesNearSeam.insert( n );
3581                 nbUseMap2++;
3582               }
3583               else
3584                 nbUseMap1++;
3585             }
3586             // for centroidalSmooth all element nodes must
3587             // be on one side of a seam
3588             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3589               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3590               nn = 0;
3591               while ( nn++ < nbn ) {
3592                 const SMDS_MeshNode* n =
3593                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3594                 setMovableNodes.erase( n );
3595               }
3596             }
3597           }
3598         } // loop on nodes on seam
3599       } // loop on edge of a face
3600     } // if ( !face.IsNull() )
3601
3602     if ( setMovableNodes.empty() ) {
3603       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3604       continue; // goto next face
3605     }
3606
3607     // -------------
3608     // SMOOTHING //
3609     // -------------
3610
3611     int it = -1;
3612     double maxRatio = -1., maxDisplacement = -1.;
3613     set<const SMDS_MeshNode*>::iterator nodeToMove;
3614     for ( it = 0; it < theNbIterations; it++ ) {
3615       maxDisplacement = 0.;
3616       nodeToMove = setMovableNodes.begin();
3617       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3618         const SMDS_MeshNode* node = (*nodeToMove);
3619         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3620
3621         // smooth
3622         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3623         if ( theSmoothMethod == LAPLACIAN )
3624           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3625         else
3626           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3627
3628         // node displacement
3629         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3630         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3631         if ( aDispl > maxDisplacement )
3632           maxDisplacement = aDispl;
3633       }
3634       // no node movement => exit
3635       //if ( maxDisplacement < 1.e-16 ) {
3636       if ( maxDisplacement < disttol ) {
3637         MESSAGE("-- no node movement --");
3638         break;
3639       }
3640
3641       // check elements quality
3642       maxRatio  = 0;
3643       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3644       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3645         const SMDS_MeshElement* elem = (*elemIt);
3646         if ( !elem || elem->GetType() != SMDSAbs_Face )
3647           continue;
3648         SMESH::Controls::TSequenceOfXYZ aPoints;
3649         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3650           double aValue = aQualityFunc.GetValue( aPoints );
3651           if ( aValue > maxRatio )
3652             maxRatio = aValue;
3653         }
3654       }
3655       if ( maxRatio <= theTgtAspectRatio ) {
3656         MESSAGE("-- quality achived --");
3657         break;
3658       }
3659       if (it+1 == theNbIterations) {
3660         MESSAGE("-- Iteration limit exceeded --");
3661       }
3662     } // smoothing iterations
3663
3664     MESSAGE(" Face id: " << *fId <<
3665             " Nb iterstions: " << it <<
3666             " Displacement: " << maxDisplacement <<
3667             " Aspect Ratio " << maxRatio);
3668
3669     // ---------------------------------------
3670     // new nodes positions are computed,
3671     // record movement in DS and set new UV
3672     // ---------------------------------------
3673     nodeToMove = setMovableNodes.begin();
3674     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3675       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3676       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3677       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3678       if ( node_uv != uvMap.end() ) {
3679         gp_XY* uv = node_uv->second;
3680         node->SetPosition
3681           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3682       }
3683     }
3684
3685     // move medium nodes of quadratic elements
3686     if ( isQuadratic )
3687     {
3688       SMESH_MesherHelper helper( *GetMesh() );
3689       helper.SetSubShape( face );
3690       vector<const SMDS_MeshNode*> nodes;
3691       bool checkUV;
3692       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3693       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
3694       {
3695         const SMDS_MeshElement* QF = *elemIt;
3696         if ( QF->IsQuadratic() )
3697         {
3698           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
3699                         SMDS_MeshElement::iterator() );
3700           nodes.push_back( nodes[0] );
3701           gp_Pnt xyz;
3702           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
3703           {
3704             if ( !surface.IsNull() )
3705             {
3706               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
3707               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
3708               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
3709               xyz = surface->Value( uv.X(), uv.Y() );
3710             }
3711             else {
3712               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
3713             }
3714             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
3715               // we have to move a medium node
3716               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
3717           }
3718         }
3719       }
3720     }
3721
3722   } // loop on face ids
3723
3724 }
3725
3726 //=======================================================================
3727 //function : isReverse
3728 //purpose  : Return true if normal of prevNodes is not co-directied with
3729 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3730 //           iNotSame is where prevNodes and nextNodes are different.
3731 //           If result is true then future volume orientation is OK
3732 //=======================================================================
3733
3734 static bool isReverse(const SMDS_MeshElement*             face,
3735                       const vector<const SMDS_MeshNode*>& prevNodes,
3736                       const vector<const SMDS_MeshNode*>& nextNodes,
3737                       const int                           iNotSame)
3738 {
3739
3740   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3741   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3742   gp_XYZ extrDir( pN - pP ), faceNorm;
3743   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
3744
3745   return faceNorm * extrDir < 0.0;
3746 }
3747
3748 //=======================================================================
3749 /*!
3750  * \brief Create elements by sweeping an element
3751  * \param elem - element to sweep
3752  * \param newNodesItVec - nodes generated from each node of the element
3753  * \param newElems - generated elements
3754  * \param nbSteps - number of sweeping steps
3755  * \param srcElements - to append elem for each generated element
3756  */
3757 //=======================================================================
3758
3759 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3760                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3761                                     list<const SMDS_MeshElement*>&        newElems,
3762                                     const int                             nbSteps,
3763                                     SMESH_SequenceOfElemPtr&              srcElements)
3764 {
3765   //MESSAGE("sweepElement " << nbSteps);
3766   SMESHDS_Mesh* aMesh = GetMeshDS();
3767
3768   const int           nbNodes = elem->NbNodes();
3769   const int         nbCorners = elem->NbCornerNodes();
3770   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3771                                                           polyhedron creation !!! */
3772   // Loop on elem nodes:
3773   // find new nodes and detect same nodes indices
3774   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3775   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3776   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3777   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3778
3779   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3780   vector<int> sames(nbNodes);
3781   vector<bool> isSingleNode(nbNodes);
3782
3783   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3784     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3785     const SMDS_MeshNode*                         node = nnIt->first;
3786     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3787     if ( listNewNodes.empty() )
3788       return;
3789
3790     itNN   [ iNode ] = listNewNodes.begin();
3791     prevNod[ iNode ] = node;
3792     nextNod[ iNode ] = listNewNodes.front();
3793
3794     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3795                                                              corner node of linear */
3796     if ( prevNod[ iNode ] != nextNod [ iNode ])
3797       nbDouble += !isSingleNode[iNode];
3798
3799     if( iNode < nbCorners ) { // check corners only
3800       if ( prevNod[ iNode ] == nextNod [ iNode ])
3801         sames[nbSame++] = iNode;
3802       else
3803         iNotSameNode = iNode;
3804     }
3805   }
3806
3807   if ( nbSame == nbNodes || nbSame > 2) {
3808     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3809     return;
3810   }
3811
3812   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3813   {
3814     // fix nodes order to have bottom normal external
3815     if ( baseType == SMDSEntity_Polygon )
3816     {
3817       std::reverse( itNN.begin(), itNN.end() );
3818       std::reverse( prevNod.begin(), prevNod.end() );
3819       std::reverse( midlNod.begin(), midlNod.end() );
3820       std::reverse( nextNod.begin(), nextNod.end() );
3821       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3822     }
3823     else
3824     {
3825       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3826       SMDS_MeshCell::applyInterlace( ind, itNN );
3827       SMDS_MeshCell::applyInterlace( ind, prevNod );
3828       SMDS_MeshCell::applyInterlace( ind, nextNod );
3829       SMDS_MeshCell::applyInterlace( ind, midlNod );
3830       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3831       if ( nbSame > 0 )
3832       {
3833         sames[nbSame] = iNotSameNode;
3834         for ( int j = 0; j <= nbSame; ++j )
3835           for ( size_t i = 0; i < ind.size(); ++i )
3836             if ( ind[i] == sames[j] )
3837             {
3838               sames[j] = i;
3839               break;
3840             }
3841         iNotSameNode = sames[nbSame];
3842       }
3843     }
3844   }
3845
3846   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3847   if ( nbSame > 0 ) {
3848     iSameNode    = sames[ nbSame-1 ];
3849     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3850     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3851     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3852   }
3853
3854   // make new elements
3855   for (int iStep = 0; iStep < nbSteps; iStep++ )
3856   {
3857     // get next nodes
3858     for ( iNode = 0; iNode < nbNodes; iNode++ )
3859     {
3860       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3861       nextNod[ iNode ] = *itNN[ iNode ]++;
3862     }
3863
3864     SMDS_MeshElement* aNewElem = 0;
3865     /*if(!elem->IsPoly())*/ {
3866       switch ( baseType ) {
3867       case SMDSEntity_0D:
3868       case SMDSEntity_Node: { // sweep NODE
3869         if ( nbSame == 0 ) {
3870           if ( isSingleNode[0] )
3871             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3872           else
3873             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3874         }
3875         else
3876           return;
3877         break;
3878       }
3879       case SMDSEntity_Edge: { // sweep EDGE
3880         if ( nbDouble == 0 )
3881         {
3882           if ( nbSame == 0 ) // ---> quadrangle
3883             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3884                                       nextNod[ 1 ], nextNod[ 0 ] );
3885           else               // ---> triangle
3886             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3887                                       nextNod[ iNotSameNode ] );
3888         }
3889         else                 // ---> polygon
3890         {
3891           vector<const SMDS_MeshNode*> poly_nodes;
3892           poly_nodes.push_back( prevNod[0] );
3893           poly_nodes.push_back( prevNod[1] );
3894           if ( prevNod[1] != nextNod[1] )
3895           {
3896             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3897             poly_nodes.push_back( nextNod[1] );
3898           }
3899           if ( prevNod[0] != nextNod[0] )
3900           {
3901             poly_nodes.push_back( nextNod[0] );
3902             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3903           }
3904           switch ( poly_nodes.size() ) {
3905           case 3:
3906             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3907             break;
3908           case 4:
3909             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3910                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3911             break;
3912           default:
3913             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3914           }
3915         }
3916         break;
3917       }
3918       case SMDSEntity_Triangle: // TRIANGLE --->
3919         {
3920           if ( nbDouble > 0 ) break;
3921           if ( nbSame == 0 )       // ---> pentahedron
3922             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3923                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3924
3925           else if ( nbSame == 1 )  // ---> pyramid
3926             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3927                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3928                                          nextNod[ iSameNode ]);
3929
3930           else // 2 same nodes:       ---> tetrahedron
3931             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3932                                          nextNod[ iNotSameNode ]);
3933           break;
3934         }
3935       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3936         {
3937           if ( nbSame == 2 )
3938             return;
3939           if ( nbDouble+nbSame == 2 )
3940           {
3941             if(nbSame==0) {      // ---> quadratic quadrangle
3942               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3943                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3944             }
3945             else { //(nbSame==1) // ---> quadratic triangle
3946               if(sames[0]==2) {
3947                 return; // medium node on axis
3948               }
3949               else if(sames[0]==0)
3950                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3951                                           nextNod[2], midlNod[1], prevNod[2]);
3952               else // sames[0]==1
3953                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3954                                           midlNod[0], nextNod[2], prevNod[2]);
3955             }
3956           }
3957           else if ( nbDouble == 3 )
3958           {
3959             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3960               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3961                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3962             }
3963           }
3964           else
3965             return;
3966           break;
3967         }
3968       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3969         if ( nbDouble > 0 ) break;
3970
3971         if ( nbSame == 0 )       // ---> hexahedron
3972           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3973                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3974
3975         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3976           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3977                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3978                                        nextNod[ iSameNode ]);
3979           newElems.push_back( aNewElem );
3980           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3981                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3982                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3983         }
3984         else if ( nbSame == 2 ) { // ---> pentahedron
3985           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3986             // iBeforeSame is same too
3987             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3988                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3989                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3990           else
3991             // iAfterSame is same too
3992             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3993                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3994                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3995         }
3996         break;
3997       }
3998       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
3999       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4000         if ( nbDouble+nbSame != 3 ) break;
4001         if(nbSame==0) {
4002           // --->  pentahedron with 15 nodes
4003           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4004                                        nextNod[0], nextNod[1], nextNod[2],
4005                                        prevNod[3], prevNod[4], prevNod[5],
4006                                        nextNod[3], nextNod[4], nextNod[5],
4007                                        midlNod[0], midlNod[1], midlNod[2]);
4008         }
4009         else if(nbSame==1) {
4010           // --->  2d order pyramid of 13 nodes
4011           int apex = iSameNode;
4012           int i0 = ( apex + 1 ) % nbCorners;
4013           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4014           int i0a = apex + 3;
4015           int i1a = i1 + 3;
4016           int i01 = i0 + 3;
4017           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4018                                       nextNod[i0], nextNod[i1], prevNod[apex],
4019                                       prevNod[i01], midlNod[i0],
4020                                       nextNod[i01], midlNod[i1],
4021                                       prevNod[i1a], prevNod[i0a],
4022                                       nextNod[i0a], nextNod[i1a]);
4023         }
4024         else if(nbSame==2) {
4025           // --->  2d order tetrahedron of 10 nodes
4026           int n1 = iNotSameNode;
4027           int n2 = ( n1 + 1             ) % nbCorners;
4028           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4029           int n12 = n1 + 3;
4030           int n23 = n2 + 3;
4031           int n31 = n3 + 3;
4032           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4033                                        prevNod[n12], prevNod[n23], prevNod[n31],
4034                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4035         }
4036         break;
4037       }
4038       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4039         if( nbSame == 0 ) {
4040           if ( nbDouble != 4 ) break;
4041           // --->  hexahedron with 20 nodes
4042           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4043                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4044                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4045                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4046                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4047         }
4048         else if(nbSame==1) {
4049           // ---> pyramid + pentahedron - can not be created since it is needed
4050           // additional middle node at the center of face
4051           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4052           return;
4053         }
4054         else if( nbSame == 2 ) {
4055           if ( nbDouble != 2 ) break;
4056           // --->  2d order Pentahedron with 15 nodes
4057           int n1,n2,n4,n5;
4058           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4059             // iBeforeSame is same too
4060             n1 = iBeforeSame;
4061             n2 = iOpposSame;
4062             n4 = iSameNode;
4063             n5 = iAfterSame;
4064           }
4065           else {
4066             // iAfterSame is same too
4067             n1 = iSameNode;
4068             n2 = iBeforeSame;
4069             n4 = iAfterSame;
4070             n5 = iOpposSame;
4071           }
4072           int n12 = n2 + 4;
4073           int n45 = n4 + 4;
4074           int n14 = n1 + 4;
4075           int n25 = n5 + 4;
4076           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4077                                        prevNod[n4], prevNod[n5], nextNod[n5],
4078                                        prevNod[n12], midlNod[n2], nextNod[n12],
4079                                        prevNod[n45], midlNod[n5], nextNod[n45],
4080                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4081         }
4082         break;
4083       }
4084       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4085
4086         if( nbSame == 0 && nbDouble == 9 ) {
4087           // --->  tri-quadratic hexahedron with 27 nodes
4088           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4089                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4090                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4091                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4092                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4093                                        prevNod[8], // bottom center
4094                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4095                                        nextNod[8], // top center
4096                                        midlNod[8]);// elem center
4097         }
4098         else
4099         {
4100           return;
4101         }
4102         break;
4103       }
4104       case SMDSEntity_Polygon: { // sweep POLYGON
4105
4106         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4107           // --->  hexagonal prism
4108           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4109                                        prevNod[3], prevNod[4], prevNod[5],
4110                                        nextNod[0], nextNod[1], nextNod[2],
4111                                        nextNod[3], nextNod[4], nextNod[5]);
4112         }
4113         break;
4114       }
4115       case SMDSEntity_Ball:
4116         return;
4117
4118       default:
4119         break;
4120       }
4121     }
4122
4123     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4124     {
4125       if ( baseType != SMDSEntity_Polygon )
4126       {
4127         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4128         SMDS_MeshCell::applyInterlace( ind, prevNod );
4129         SMDS_MeshCell::applyInterlace( ind, nextNod );
4130         SMDS_MeshCell::applyInterlace( ind, midlNod );
4131         SMDS_MeshCell::applyInterlace( ind, itNN );
4132         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4133         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4134       }
4135       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4136       vector<int> quantities (nbNodes + 2);
4137       polyedre_nodes.clear();
4138       quantities.clear();
4139
4140       // bottom of prism
4141       for (int inode = 0; inode < nbNodes; inode++)
4142         polyedre_nodes.push_back( prevNod[inode] );
4143       quantities.push_back( nbNodes );
4144
4145       // top of prism
4146       polyedre_nodes.push_back( nextNod[0] );
4147       for (int inode = nbNodes; inode-1; --inode )
4148         polyedre_nodes.push_back( nextNod[inode-1] );
4149       quantities.push_back( nbNodes );
4150
4151       // side faces
4152       for (int iface = 0; iface < nbNodes; iface++)
4153       {
4154         const int prevNbNodes = polyedre_nodes.size();
4155         int inextface = (iface+1) % nbNodes;
4156         polyedre_nodes.push_back( prevNod[inextface] );
4157         polyedre_nodes.push_back( prevNod[iface] );
4158         if ( prevNod[iface] != nextNod[iface] )
4159         {
4160           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4161           polyedre_nodes.push_back( nextNod[iface] );
4162         }
4163         if ( prevNod[inextface] != nextNod[inextface] )
4164         {
4165           polyedre_nodes.push_back( nextNod[inextface] );
4166           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4167         }
4168         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4169         if ( nbFaceNodes > 2 )
4170           quantities.push_back( nbFaceNodes );
4171         else // degenerated face
4172           polyedre_nodes.resize( prevNbNodes );
4173       }
4174       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4175     }
4176
4177     if ( aNewElem ) {
4178       newElems.push_back( aNewElem );
4179       myLastCreatedElems.Append(aNewElem);
4180       srcElements.Append( elem );
4181     }
4182
4183     // set new prev nodes
4184     for ( iNode = 0; iNode < nbNodes; iNode++ )
4185       prevNod[ iNode ] = nextNod[ iNode ];
4186
4187   } // for steps
4188 }
4189
4190 //=======================================================================
4191 /*!
4192  * \brief Create 1D and 2D elements around swept elements
4193  * \param mapNewNodes - source nodes and ones generated from them
4194  * \param newElemsMap - source elements and ones generated from them
4195  * \param elemNewNodesMap - nodes generated from each node of each element
4196  * \param elemSet - all swept elements
4197  * \param nbSteps - number of sweeping steps
4198  * \param srcElements - to append elem for each generated element
4199  */
4200 //=======================================================================
4201
4202 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4203                                   TElemOfElemListMap &     newElemsMap,
4204                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4205                                   TIDSortedElemSet&        elemSet,
4206                                   const int                nbSteps,
4207                                   SMESH_SequenceOfElemPtr& srcElements)
4208 {
4209   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4210   SMESHDS_Mesh* aMesh = GetMeshDS();
4211
4212   // Find nodes belonging to only one initial element - sweep them into edges.
4213
4214   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4215   for ( ; nList != mapNewNodes.end(); nList++ )
4216   {
4217     const SMDS_MeshNode* node =
4218       static_cast<const SMDS_MeshNode*>( nList->first );
4219     if ( newElemsMap.count( node ))
4220       continue; // node was extruded into edge
4221     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4222     int nbInitElems = 0;
4223     const SMDS_MeshElement* el = 0;
4224     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4225     while ( eIt->more() && nbInitElems < 2 ) {
4226       el = eIt->next();
4227       SMDSAbs_ElementType type = el->GetType();
4228       if ( type == SMDSAbs_Volume || type < highType ) continue;
4229       if ( type > highType ) {
4230         nbInitElems = 0;
4231         highType = type;
4232       }
4233       nbInitElems += elemSet.count(el);
4234     }
4235     if ( nbInitElems < 2 ) {
4236       bool NotCreateEdge = el && el->IsMediumNode(node);
4237       if(!NotCreateEdge) {
4238         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4239         list<const SMDS_MeshElement*> newEdges;
4240         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4241       }
4242     }
4243   }
4244
4245   // Make a ceiling for each element ie an equal element of last new nodes.
4246   // Find free links of faces - make edges and sweep them into faces.
4247
4248   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4249   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4250   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4251   {
4252     const SMDS_MeshElement* elem = itElem->first;
4253     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4254
4255     if(itElem->second.size()==0) continue;
4256
4257     const bool isQuadratic = elem->IsQuadratic();
4258
4259     if ( elem->GetType() == SMDSAbs_Edge ) {
4260       // create a ceiling edge
4261       if ( !isQuadratic ) {
4262         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4263                                vecNewNodes[ 1 ]->second.back())) {
4264           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4265                                                    vecNewNodes[ 1 ]->second.back()));
4266           srcElements.Append( elem );
4267         }
4268       }
4269       else {
4270         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4271                                vecNewNodes[ 1 ]->second.back(),
4272                                vecNewNodes[ 2 ]->second.back())) {
4273           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4274                                                    vecNewNodes[ 1 ]->second.back(),
4275                                                    vecNewNodes[ 2 ]->second.back()));
4276           srcElements.Append( elem );
4277         }
4278       }
4279     }
4280     if ( elem->GetType() != SMDSAbs_Face )
4281       continue;
4282
4283     bool hasFreeLinks = false;
4284
4285     TIDSortedElemSet avoidSet;
4286     avoidSet.insert( elem );
4287
4288     set<const SMDS_MeshNode*> aFaceLastNodes;
4289     int iNode, nbNodes = vecNewNodes.size();
4290     if ( !isQuadratic ) {
4291       // loop on the face nodes
4292       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4293         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4294         // look for free links of the face
4295         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4296         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4297         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4298         // check if a link n1-n2 is free
4299         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4300           hasFreeLinks = true;
4301           // make a new edge and a ceiling for a new edge
4302           const SMDS_MeshElement* edge;
4303           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4304             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4305             srcElements.Append( myLastCreatedElems.Last() );
4306           }
4307           n1 = vecNewNodes[ iNode ]->second.back();
4308           n2 = vecNewNodes[ iNext ]->second.back();
4309           if ( !aMesh->FindEdge( n1, n2 )) {
4310             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4311             srcElements.Append( edge );
4312           }
4313         }
4314       }
4315     }
4316     else { // elem is quadratic face
4317       int nbn = nbNodes/2;
4318       for ( iNode = 0; iNode < nbn; iNode++ ) {
4319         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4320         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4321         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4322         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4323         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4324         // check if a link is free
4325         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4326              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4327              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4328           hasFreeLinks = true;
4329           // make an edge and a ceiling for a new edge
4330           // find medium node
4331           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4332             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4333             srcElements.Append( elem );
4334           }
4335           n1 = vecNewNodes[ iNode ]->second.back();
4336           n2 = vecNewNodes[ iNext ]->second.back();
4337           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4338           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4339             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4340             srcElements.Append( elem );
4341           }
4342         }
4343       }
4344       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4345         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4346       }
4347     }
4348
4349     // sweep free links into faces
4350
4351     if ( hasFreeLinks )  {
4352       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4353       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4354
4355       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4356       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4357       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4358         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4359         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4360       }
4361       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4362         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4363         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4364       }
4365       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4366         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4367         std::advance( v, volNb );
4368         // find indices of free faces of a volume and their source edges
4369         list< int > freeInd;
4370         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4371         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4372         int iF, nbF = vTool.NbFaces();
4373         for ( iF = 0; iF < nbF; iF ++ ) {
4374           if (vTool.IsFreeFace( iF ) &&
4375               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4376               initNodeSet != faceNodeSet) // except an initial face
4377           {
4378             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4379               continue;
4380             if ( faceNodeSet == initNodeSetNoCenter )
4381               continue;
4382             freeInd.push_back( iF );
4383             // find source edge of a free face iF
4384             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4385             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4386             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4387                                    initNodeSet.begin(), initNodeSet.end(),
4388                                    commonNodes.begin());
4389             if ( (*v)->IsQuadratic() )
4390               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4391             else
4392               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4393 #ifdef _DEBUG_
4394             if ( !srcEdges.back() )
4395             {
4396               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4397                    << iF << " of volume #" << vTool.ID() << endl;
4398             }
4399 #endif
4400           }
4401         }
4402         if ( freeInd.empty() )
4403           continue;
4404
4405         // create faces for all steps;
4406         // if such a face has been already created by sweep of edge,
4407         // assure that its orientation is OK
4408         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4409           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4410           vTool.SetExternalNormal();
4411           const int nextShift = vTool.IsForward() ? +1 : -1;
4412           list< int >::iterator ind = freeInd.begin();
4413           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4414           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4415           {
4416             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4417             int nbn = vTool.NbFaceNodes( *ind );
4418             const SMDS_MeshElement * f = 0;
4419             if ( nbn == 3 )              ///// triangle
4420             {
4421               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4422               if ( !f ||
4423                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4424               {
4425                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4426                                                      nodes[ 1 ],
4427                                                      nodes[ 1 + nextShift ] };
4428                 if ( f )
4429                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4430                 else
4431                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4432                                                             newOrder[ 2 ] ));
4433               }
4434             }
4435             else if ( nbn == 4 )       ///// quadrangle
4436             {
4437               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4438               if ( !f ||
4439                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4440               {
4441                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4442                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4443                 if ( f )
4444                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4445                 else
4446                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4447                                                             newOrder[ 2 ], newOrder[ 3 ]));
4448               }
4449             }
4450             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4451             {
4452               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4453               if ( !f ||
4454                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4455               {
4456                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4457                                                      nodes[2],
4458                                                      nodes[2 + 2*nextShift],
4459                                                      nodes[3 - 2*nextShift],
4460                                                      nodes[3],
4461                                                      nodes[3 + 2*nextShift]};
4462                 if ( f )
4463                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4464                 else
4465                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4466                                                             newOrder[ 1 ],
4467                                                             newOrder[ 2 ],
4468                                                             newOrder[ 3 ],
4469                                                             newOrder[ 4 ],
4470                                                             newOrder[ 5 ] ));
4471               }
4472             }
4473             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4474             {
4475               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4476                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4477               if ( !f ||
4478                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4479               {
4480                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4481                                                      nodes[4 - 2*nextShift],
4482                                                      nodes[4],
4483                                                      nodes[4 + 2*nextShift],
4484                                                      nodes[1],
4485                                                      nodes[5 - 2*nextShift],
4486                                                      nodes[5],
4487                                                      nodes[5 + 2*nextShift] };
4488                 if ( f )
4489                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4490                 else
4491                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4492                                                            newOrder[ 2 ], newOrder[ 3 ],
4493                                                            newOrder[ 4 ], newOrder[ 5 ],
4494                                                            newOrder[ 6 ], newOrder[ 7 ]));
4495               }
4496             }
4497             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4498             {
4499               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4500                                       SMDSAbs_Face, /*noMedium=*/false);
4501               if ( !f ||
4502                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4503               {
4504                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4505                                                      nodes[4 - 2*nextShift],
4506                                                      nodes[4],
4507                                                      nodes[4 + 2*nextShift],
4508                                                      nodes[1],
4509                                                      nodes[5 - 2*nextShift],
4510                                                      nodes[5],
4511                                                      nodes[5 + 2*nextShift],
4512                                                      nodes[8] };
4513                 if ( f )
4514                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4515                 else
4516                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4517                                                            newOrder[ 2 ], newOrder[ 3 ],
4518                                                            newOrder[ 4 ], newOrder[ 5 ],
4519                                                            newOrder[ 6 ], newOrder[ 7 ],
4520                                                            newOrder[ 8 ]));
4521               }
4522             }
4523             else  //////// polygon
4524             {
4525               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4526               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4527               if ( !f ||
4528                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4529               {
4530                 if ( !vTool.IsForward() )
4531                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4532                 if ( f )
4533                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4534                 else
4535                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4536               }
4537             }
4538
4539             while ( srcElements.Length() < myLastCreatedElems.Length() )
4540               srcElements.Append( *srcEdge );
4541
4542           }  // loop on free faces
4543
4544           // go to the next volume
4545           iVol = 0;
4546           while ( iVol++ < nbVolumesByStep ) v++;
4547
4548         } // loop on steps
4549       } // loop on volumes of one step
4550     } // sweep free links into faces
4551
4552     // Make a ceiling face with a normal external to a volume
4553
4554     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4555     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4556     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4557
4558     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4559       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4560       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4561     }
4562     if ( iF >= 0 ) {
4563       lastVol.SetExternalNormal();
4564       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4565       int nbn = lastVol.NbFaceNodes( iF );
4566       // we do not use this->AddElement() because nodes are interlaced
4567       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4568       if ( !hasFreeLinks ||
4569            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4570       {
4571         if ( nbn == 3 )
4572           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4573
4574         else if ( nbn == 4 )
4575           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4576
4577         else if ( nbn == 6 && isQuadratic )
4578           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4579                                                     nodes[1], nodes[3], nodes[5]));
4580         else if ( nbn == 7 && isQuadratic )
4581           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4582                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
4583         else if ( nbn == 8 && isQuadratic )
4584           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4585                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
4586         else if ( nbn == 9 && isQuadratic )
4587           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4588                                                     nodes[1], nodes[3], nodes[5], nodes[7],
4589                                                     nodes[8]));
4590         else
4591           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
4592
4593         while ( srcElements.Length() < myLastCreatedElems.Length() )
4594           srcElements.Append( elem );
4595       }
4596     }
4597   } // loop on swept elements
4598 }
4599
4600 //=======================================================================
4601 //function : RotationSweep
4602 //purpose  :
4603 //=======================================================================
4604
4605 SMESH_MeshEditor::PGroupIDs
4606 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4607                                 const gp_Ax1&      theAxis,
4608                                 const double       theAngle,
4609                                 const int          theNbSteps,
4610                                 const double       theTol,
4611                                 const bool         theMakeGroups,
4612                                 const bool         theMakeWalls)
4613 {
4614   myLastCreatedElems.Clear();
4615   myLastCreatedNodes.Clear();
4616
4617   // source elements for each generated one
4618   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4619
4620   MESSAGE( "RotationSweep()");
4621   gp_Trsf aTrsf;
4622   aTrsf.SetRotation( theAxis, theAngle );
4623   gp_Trsf aTrsf2;
4624   aTrsf2.SetRotation( theAxis, theAngle/2. );
4625
4626   gp_Lin aLine( theAxis );
4627   double aSqTol = theTol * theTol;
4628
4629   SMESHDS_Mesh* aMesh = GetMeshDS();
4630
4631   TNodeOfNodeListMap mapNewNodes;
4632   TElemOfVecOfNnlmiMap mapElemNewNodes;
4633   TElemOfElemListMap newElemsMap;
4634
4635   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4636                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4637                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4638   // loop on theElems
4639   TIDSortedElemSet::iterator itElem;
4640   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4641     const SMDS_MeshElement* elem = *itElem;
4642     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4643       continue;
4644     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4645     newNodesItVec.reserve( elem->NbNodes() );
4646
4647     // loop on elem nodes
4648     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4649     while ( itN->more() )
4650     {
4651       // check if a node has been already sweeped
4652       const SMDS_MeshNode* node = cast2Node( itN->next() );
4653
4654       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4655       double coord[3];
4656       aXYZ.Coord( coord[0], coord[1], coord[2] );
4657       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4658
4659       TNodeOfNodeListMapItr nIt =
4660         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4661       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4662       if ( listNewNodes.empty() )
4663       {
4664         // check if we are to create medium nodes between corner ones
4665         bool needMediumNodes = false;
4666         if ( isQuadraticMesh )
4667         {
4668           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4669           while (it->more() && !needMediumNodes )
4670           {
4671             const SMDS_MeshElement* invElem = it->next();
4672             if ( invElem != elem && !theElems.count( invElem )) continue;
4673             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4674             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4675               needMediumNodes = true;
4676           }
4677         }
4678
4679         // make new nodes
4680         const SMDS_MeshNode * newNode = node;
4681         for ( int i = 0; i < theNbSteps; i++ ) {
4682           if ( !isOnAxis ) {
4683             if ( needMediumNodes )  // create a medium node
4684             {
4685               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4686               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4687               myLastCreatedNodes.Append(newNode);
4688               srcNodes.Append( node );
4689               listNewNodes.push_back( newNode );
4690               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4691             }
4692             else {
4693               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4694             }
4695             // create a corner node
4696             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4697             myLastCreatedNodes.Append(newNode);
4698             srcNodes.Append( node );
4699             listNewNodes.push_back( newNode );
4700           }
4701           else {
4702             listNewNodes.push_back( newNode );
4703             // if ( needMediumNodes )
4704             //   listNewNodes.push_back( newNode );
4705           }
4706         }
4707       }
4708       newNodesItVec.push_back( nIt );
4709     }
4710     // make new elements
4711     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4712   }
4713
4714   if ( theMakeWalls )
4715     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4716
4717   PGroupIDs newGroupIDs;
4718   if ( theMakeGroups )
4719     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4720
4721   return newGroupIDs;
4722 }
4723
4724
4725 //=======================================================================
4726 //function : CreateNode
4727 //purpose  :
4728 //=======================================================================
4729 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4730                                                   const double y,
4731                                                   const double z,
4732                                                   const double tolnode,
4733                                                   SMESH_SequenceOfNode& aNodes)
4734 {
4735   // myLastCreatedElems.Clear();
4736   // myLastCreatedNodes.Clear();
4737
4738   gp_Pnt P1(x,y,z);
4739   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4740
4741   // try to search in sequence of existing nodes
4742   // if aNodes.Length()>0 we 'nave to use given sequence
4743   // else - use all nodes of mesh
4744   if(aNodes.Length()>0) {
4745     int i;
4746     for(i=1; i<=aNodes.Length(); i++) {
4747       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4748       if(P1.Distance(P2)<tolnode)
4749         return aNodes.Value(i);
4750     }
4751   }
4752   else {
4753     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4754     while(itn->more()) {
4755       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4756       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4757       if(P1.Distance(P2)<tolnode)
4758         return aN;
4759     }
4760   }
4761
4762   // create new node and return it
4763   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4764   //myLastCreatedNodes.Append(NewNode);
4765   return NewNode;
4766 }
4767
4768
4769 //=======================================================================
4770 //function : ExtrusionSweep
4771 //purpose  :
4772 //=======================================================================
4773
4774 SMESH_MeshEditor::PGroupIDs
4775 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4776                                   const gp_Vec&       theStep,
4777                                   const int           theNbSteps,
4778                                   TElemOfElemListMap& newElemsMap,
4779                                   const bool          theMakeGroups,
4780                                   const int           theFlags,
4781                                   const double        theTolerance)
4782 {
4783   ExtrusParam aParams;
4784   aParams.myDir = gp_Dir(theStep);
4785   aParams.myNodes.Clear();
4786   aParams.mySteps = new TColStd_HSequenceOfReal;
4787   int i;
4788   for(i=1; i<=theNbSteps; i++)
4789     aParams.mySteps->Append(theStep.Magnitude());
4790
4791   return
4792     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4793 }
4794
4795
4796 //=======================================================================
4797 //function : ExtrusionSweep
4798 //purpose  :
4799 //=======================================================================
4800
4801 SMESH_MeshEditor::PGroupIDs
4802 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4803                                   ExtrusParam&        theParams,
4804                                   TElemOfElemListMap& newElemsMap,
4805                                   const bool          theMakeGroups,
4806                                   const int           theFlags,
4807                                   const double        theTolerance)
4808 {
4809   myLastCreatedElems.Clear();
4810   myLastCreatedNodes.Clear();
4811
4812   // source elements for each generated one
4813   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4814
4815   SMESHDS_Mesh* aMesh = GetMeshDS();
4816
4817   int nbsteps = theParams.mySteps->Length();
4818
4819   TNodeOfNodeListMap mapNewNodes;
4820   //TNodeOfNodeVecMap mapNewNodes;
4821   TElemOfVecOfNnlmiMap mapElemNewNodes;
4822   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4823
4824   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4825                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4826                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4827   // loop on theElems
4828   TIDSortedElemSet::iterator itElem;
4829   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4830     // check element type
4831     const SMDS_MeshElement* elem = *itElem;
4832     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4833       continue;
4834
4835     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4836     newNodesItVec.reserve( elem->NbNodes() );
4837
4838     // loop on elem nodes
4839     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4840     while ( itN->more() )
4841     {
4842       // check if a node has been already sweeped
4843       const SMDS_MeshNode* node = cast2Node( itN->next() );
4844       TNodeOfNodeListMap::iterator nIt =
4845         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4846       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4847       if ( listNewNodes.empty() )
4848       {
4849         // make new nodes
4850
4851         // check if we are to create medium nodes between corner ones
4852         bool needMediumNodes = false;
4853         if ( isQuadraticMesh )
4854         {
4855           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4856           while (it->more() && !needMediumNodes )
4857           {
4858             const SMDS_MeshElement* invElem = it->next();
4859             if ( invElem != elem && !theElems.count( invElem )) continue;
4860             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4861             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4862               needMediumNodes = true;
4863           }
4864         }
4865
4866         double coord[] = { node->X(), node->Y(), node->Z() };
4867         for ( int i = 0; i < nbsteps; i++ )
4868         {
4869           if ( needMediumNodes ) // create a medium node
4870           {
4871             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4872             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4873             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4874             if( theFlags & EXTRUSION_FLAG_SEW ) {
4875               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4876                                                          theTolerance, theParams.myNodes);
4877               listNewNodes.push_back( newNode );
4878             }
4879             else {
4880               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4881               myLastCreatedNodes.Append(newNode);
4882               srcNodes.Append( node );
4883               listNewNodes.push_back( newNode );
4884             }
4885           }
4886           // create a corner node
4887           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4888           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4889           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4890           if( theFlags & EXTRUSION_FLAG_SEW ) {
4891             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4892                                                        theTolerance, theParams.myNodes);
4893             listNewNodes.push_back( newNode );
4894           }
4895           else {
4896             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4897             myLastCreatedNodes.Append(newNode);
4898             srcNodes.Append( node );
4899             listNewNodes.push_back( newNode );
4900           }
4901         }
4902       }
4903       newNodesItVec.push_back( nIt );
4904     }
4905     // make new elements
4906     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4907   }
4908
4909   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4910     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4911   }
4912   PGroupIDs newGroupIDs;
4913   if ( theMakeGroups )
4914     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4915
4916   return newGroupIDs;
4917 }
4918
4919 //=======================================================================
4920 //function : ExtrusionAlongTrack
4921 //purpose  :
4922 //=======================================================================
4923 SMESH_MeshEditor::Extrusion_Error
4924 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4925                                        SMESH_subMesh*       theTrack,
4926                                        const SMDS_MeshNode* theN1,
4927                                        const bool           theHasAngles,
4928                                        list<double>&        theAngles,
4929                                        const bool           theLinearVariation,
4930                                        const bool           theHasRefPoint,
4931                                        const gp_Pnt&        theRefPoint,
4932                                        const bool           theMakeGroups)
4933 {
4934   MESSAGE("ExtrusionAlongTrack");
4935   myLastCreatedElems.Clear();
4936   myLastCreatedNodes.Clear();
4937
4938   int aNbE;
4939   std::list<double> aPrms;
4940   TIDSortedElemSet::iterator itElem;
4941
4942   gp_XYZ aGC;
4943   TopoDS_Edge aTrackEdge;
4944   TopoDS_Vertex aV1, aV2;
4945
4946   SMDS_ElemIteratorPtr aItE;
4947   SMDS_NodeIteratorPtr aItN;
4948   SMDSAbs_ElementType aTypeE;
4949
4950   TNodeOfNodeListMap mapNewNodes;
4951
4952   // 1. Check data
4953   aNbE = theElements.size();
4954   // nothing to do
4955   if ( !aNbE )
4956     return EXTR_NO_ELEMENTS;
4957
4958   // 1.1 Track Pattern
4959   ASSERT( theTrack );
4960
4961   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4962
4963   aItE = pSubMeshDS->GetElements();
4964   while ( aItE->more() ) {
4965     const SMDS_MeshElement* pE = aItE->next();
4966     aTypeE = pE->GetType();
4967     // Pattern must contain links only
4968     if ( aTypeE != SMDSAbs_Edge )
4969       return EXTR_PATH_NOT_EDGE;
4970   }
4971
4972   list<SMESH_MeshEditor_PathPoint> fullList;
4973
4974   const TopoDS_Shape& aS = theTrack->GetSubShape();
4975   // Sub-shape for the Pattern must be an Edge or Wire
4976   if( aS.ShapeType() == TopAbs_EDGE ) {
4977     aTrackEdge = TopoDS::Edge( aS );
4978     // the Edge must not be degenerated
4979     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4980       return EXTR_BAD_PATH_SHAPE;
4981     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4982     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4983     const SMDS_MeshNode* aN1 = aItN->next();
4984     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4985     const SMDS_MeshNode* aN2 = aItN->next();
4986     // starting node must be aN1 or aN2
4987     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4988       return EXTR_BAD_STARTING_NODE;
4989     aItN = pSubMeshDS->GetNodes();
4990     while ( aItN->more() ) {
4991       const SMDS_MeshNode* pNode = aItN->next();
4992       const SMDS_EdgePosition* pEPos =
4993         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4994       double aT = pEPos->GetUParameter();
4995       aPrms.push_back( aT );
4996     }
4997     //Extrusion_Error err =
4998     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4999   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5000     list< SMESH_subMesh* > LSM;
5001     TopTools_SequenceOfShape Edges;
5002     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5003     while(itSM->more()) {
5004       SMESH_subMesh* SM = itSM->next();
5005       LSM.push_back(SM);
5006       const TopoDS_Shape& aS = SM->GetSubShape();
5007       Edges.Append(aS);
5008     }
5009     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5010     int startNid = theN1->GetID();
5011     TColStd_MapOfInteger UsedNums;
5012
5013     int NbEdges = Edges.Length();
5014     int i = 1;
5015     for(; i<=NbEdges; i++) {
5016       int k = 0;
5017       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5018       for(; itLSM!=LSM.end(); itLSM++) {
5019         k++;
5020         if(UsedNums.Contains(k)) continue;
5021         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5022         SMESH_subMesh* locTrack = *itLSM;
5023         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5024         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5025         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5026         const SMDS_MeshNode* aN1 = aItN->next();
5027         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5028         const SMDS_MeshNode* aN2 = aItN->next();
5029         // starting node must be aN1 or aN2
5030         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5031         // 2. Collect parameters on the track edge
5032         aPrms.clear();
5033         aItN = locMeshDS->GetNodes();
5034         while ( aItN->more() ) {
5035           const SMDS_MeshNode* pNode = aItN->next();
5036           const SMDS_EdgePosition* pEPos =
5037             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5038           double aT = pEPos->GetUParameter();
5039           aPrms.push_back( aT );
5040         }
5041         list<SMESH_MeshEditor_PathPoint> LPP;
5042         //Extrusion_Error err =
5043         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5044         LLPPs.push_back(LPP);
5045         UsedNums.Add(k);
5046         // update startN for search following egde
5047         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5048         else startNid = aN1->GetID();
5049         break;
5050       }
5051     }
5052     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5053     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5054     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5055     for(; itPP!=firstList.end(); itPP++) {
5056       fullList.push_back( *itPP );
5057     }
5058     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5059     fullList.pop_back();
5060     itLLPP++;
5061     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5062       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5063       itPP = currList.begin();
5064       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5065       gp_Dir D1 = PP1.Tangent();
5066       gp_Dir D2 = PP2.Tangent();
5067       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5068                            (D1.Z()+D2.Z())/2 ) );
5069       PP1.SetTangent(Dnew);
5070       fullList.push_back(PP1);
5071       itPP++;
5072       for(; itPP!=firstList.end(); itPP++) {
5073         fullList.push_back( *itPP );
5074       }
5075       PP1 = fullList.back();
5076       fullList.pop_back();
5077     }
5078     // if wire not closed
5079     fullList.push_back(PP1);
5080     // else ???
5081   }
5082   else {
5083     return EXTR_BAD_PATH_SHAPE;
5084   }
5085
5086   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5087                           theHasRefPoint, theRefPoint, theMakeGroups);
5088 }
5089
5090
5091 //=======================================================================
5092 //function : ExtrusionAlongTrack
5093 //purpose  :
5094 //=======================================================================
5095 SMESH_MeshEditor::Extrusion_Error
5096 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5097                                        SMESH_Mesh*          theTrack,
5098                                        const SMDS_MeshNode* theN1,
5099                                        const bool           theHasAngles,
5100                                        list<double>&        theAngles,
5101                                        const bool           theLinearVariation,
5102                                        const bool           theHasRefPoint,
5103                                        const gp_Pnt&        theRefPoint,
5104                                        const bool           theMakeGroups)
5105 {
5106   myLastCreatedElems.Clear();
5107   myLastCreatedNodes.Clear();
5108
5109   int aNbE;
5110   std::list<double> aPrms;
5111   TIDSortedElemSet::iterator itElem;
5112
5113   gp_XYZ aGC;
5114   TopoDS_Edge aTrackEdge;
5115   TopoDS_Vertex aV1, aV2;
5116
5117   SMDS_ElemIteratorPtr aItE;
5118   SMDS_NodeIteratorPtr aItN;
5119   SMDSAbs_ElementType aTypeE;
5120
5121   TNodeOfNodeListMap mapNewNodes;
5122
5123   // 1. Check data
5124   aNbE = theElements.size();
5125   // nothing to do
5126   if ( !aNbE )
5127     return EXTR_NO_ELEMENTS;
5128
5129   // 1.1 Track Pattern
5130   ASSERT( theTrack );
5131
5132   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5133
5134   aItE = pMeshDS->elementsIterator();
5135   while ( aItE->more() ) {
5136     const SMDS_MeshElement* pE = aItE->next();
5137     aTypeE = pE->GetType();
5138     // Pattern must contain links only
5139     if ( aTypeE != SMDSAbs_Edge )
5140       return EXTR_PATH_NOT_EDGE;
5141   }
5142
5143   list<SMESH_MeshEditor_PathPoint> fullList;
5144
5145   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5146
5147   if ( !theTrack->HasShapeToMesh() ) {
5148     //Mesh without shape
5149     const SMDS_MeshNode* currentNode = NULL;
5150     const SMDS_MeshNode* prevNode = theN1;
5151     std::vector<const SMDS_MeshNode*> aNodesList;
5152     aNodesList.push_back(theN1);
5153     int nbEdges = 0, conn=0;
5154     const SMDS_MeshElement* prevElem = NULL;
5155     const SMDS_MeshElement* currentElem = NULL;
5156     int totalNbEdges = theTrack->NbEdges();
5157     SMDS_ElemIteratorPtr nIt;
5158
5159     //check start node
5160     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5161       return EXTR_BAD_STARTING_NODE;
5162     }
5163
5164     conn = nbEdgeConnectivity(theN1);
5165     if(conn > 2)
5166       return EXTR_PATH_NOT_EDGE;
5167
5168     aItE = theN1->GetInverseElementIterator();
5169     prevElem = aItE->next();
5170     currentElem = prevElem;
5171     //Get all nodes
5172     if(totalNbEdges == 1 ) {
5173       nIt = currentElem->nodesIterator();
5174       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5175       if(currentNode == prevNode)
5176         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5177       aNodesList.push_back(currentNode);
5178     } else {
5179       nIt = currentElem->nodesIterator();
5180       while( nIt->more() ) {
5181         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5182         if(currentNode == prevNode)
5183           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5184         aNodesList.push_back(currentNode);
5185
5186         //case of the closed mesh
5187         if(currentNode == theN1) {
5188           nbEdges++;
5189           break;
5190         }
5191
5192         conn = nbEdgeConnectivity(currentNode);
5193         if(conn > 2) {
5194           return EXTR_PATH_NOT_EDGE;
5195         }else if( conn == 1 && nbEdges > 0 ) {
5196           //End of the path
5197           nbEdges++;
5198           break;
5199         }else {
5200           prevNode = currentNode;
5201           aItE = currentNode->GetInverseElementIterator();
5202           currentElem = aItE->next();
5203           if( currentElem  == prevElem)
5204             currentElem = aItE->next();
5205           nIt = currentElem->nodesIterator();
5206           prevElem = currentElem;
5207           nbEdges++;
5208         }
5209       }
5210     }
5211
5212     if(nbEdges != totalNbEdges)
5213       return EXTR_PATH_NOT_EDGE;
5214
5215     TopTools_SequenceOfShape Edges;
5216     double x1,x2,y1,y2,z1,z2;
5217     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218     int startNid = theN1->GetID();
5219     for(int i = 1; i < aNodesList.size(); i++) {
5220       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5221       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5222       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5223       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5224       list<SMESH_MeshEditor_PathPoint> LPP;
5225       aPrms.clear();
5226       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5227       LLPPs.push_back(LPP);
5228       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5229       else startNid = aNodesList[i-1]->GetID();
5230
5231     }
5232
5233     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5234     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5235     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5236     for(; itPP!=firstList.end(); itPP++) {
5237       fullList.push_back( *itPP );
5238     }
5239
5240     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5241     SMESH_MeshEditor_PathPoint PP2;
5242     fullList.pop_back();
5243     itLLPP++;
5244     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5245       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5246       itPP = currList.begin();
5247       PP2 = currList.front();
5248       gp_Dir D1 = PP1.Tangent();
5249       gp_Dir D2 = PP2.Tangent();
5250       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5251                            (D1.Z()+D2.Z())/2 ) );
5252       PP1.SetTangent(Dnew);
5253       fullList.push_back(PP1);
5254       itPP++;
5255       for(; itPP!=currList.end(); itPP++) {
5256         fullList.push_back( *itPP );
5257       }
5258       PP1 = fullList.back();
5259       fullList.pop_back();
5260     }
5261     fullList.push_back(PP1);
5262
5263   } // Sub-shape for the Pattern must be an Edge or Wire
5264   else if( aS.ShapeType() == TopAbs_EDGE ) {
5265     aTrackEdge = TopoDS::Edge( aS );
5266     // the Edge must not be degenerated
5267     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5268       return EXTR_BAD_PATH_SHAPE;
5269     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5270     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5271     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5272     // starting node must be aN1 or aN2
5273     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5274       return EXTR_BAD_STARTING_NODE;
5275     aItN = pMeshDS->nodesIterator();
5276     while ( aItN->more() ) {
5277       const SMDS_MeshNode* pNode = aItN->next();
5278       if( pNode==aN1 || pNode==aN2 ) continue;
5279       const SMDS_EdgePosition* pEPos =
5280         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5281       double aT = pEPos->GetUParameter();
5282       aPrms.push_back( aT );
5283     }
5284     //Extrusion_Error err =
5285     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5286   }
5287   else if( aS.ShapeType() == TopAbs_WIRE ) {
5288     list< SMESH_subMesh* > LSM;
5289     TopTools_SequenceOfShape Edges;
5290     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5291     for(; eExp.More(); eExp.Next()) {
5292       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5293       if( BRep_Tool::Degenerated(E) ) continue;
5294       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5295       if(SM) {
5296         LSM.push_back(SM);
5297         Edges.Append(E);
5298       }
5299     }
5300     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5301     TopoDS_Vertex aVprev;
5302     TColStd_MapOfInteger UsedNums;
5303     int NbEdges = Edges.Length();
5304     int i = 1;
5305     for(; i<=NbEdges; i++) {
5306       int k = 0;
5307       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5308       for(; itLSM!=LSM.end(); itLSM++) {
5309         k++;
5310         if(UsedNums.Contains(k)) continue;
5311         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5312         SMESH_subMesh* locTrack = *itLSM;
5313         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5314         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5315         bool aN1isOK = false, aN2isOK = false;
5316         if ( aVprev.IsNull() ) {
5317           // if previous vertex is not yet defined, it means that we in the beginning of wire
5318           // and we have to find initial vertex corresponding to starting node theN1
5319           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5320           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5321           // starting node must be aN1 or aN2
5322           aN1isOK = ( aN1 && aN1 == theN1 );
5323           aN2isOK = ( aN2 && aN2 == theN1 );
5324         }
5325         else {
5326           // we have specified ending vertex of the previous edge on the previous iteration
5327           // and we have just to check that it corresponds to any vertex in current segment
5328           aN1isOK = aVprev.IsSame( aV1 );
5329           aN2isOK = aVprev.IsSame( aV2 );
5330         }
5331         if ( !aN1isOK && !aN2isOK ) continue;
5332         // 2. Collect parameters on the track edge
5333         aPrms.clear();
5334         aItN = locMeshDS->GetNodes();
5335         while ( aItN->more() ) {
5336           const SMDS_MeshNode*     pNode = aItN->next();
5337           const SMDS_EdgePosition* pEPos =
5338             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5339           double aT = pEPos->GetUParameter();
5340           aPrms.push_back( aT );
5341         }
5342         list<SMESH_MeshEditor_PathPoint> LPP;
5343         //Extrusion_Error err =
5344         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5345         LLPPs.push_back(LPP);
5346         UsedNums.Add(k);
5347         // update startN for search following egde
5348         if ( aN1isOK ) aVprev = aV2;
5349         else           aVprev = aV1;
5350         break;
5351       }
5352     }
5353     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5354     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5355     fullList.splice( fullList.end(), firstList );
5356
5357     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5358     fullList.pop_back();
5359     itLLPP++;
5360     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5361       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5362       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5363       gp_Dir D1 = PP1.Tangent();
5364       gp_Dir D2 = PP2.Tangent();
5365       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5366       PP1.SetTangent(Dnew);
5367       fullList.push_back(PP1);
5368       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5369       PP1 = fullList.back();
5370       fullList.pop_back();
5371     }
5372     // if wire not closed
5373     fullList.push_back(PP1);
5374     // else ???
5375   }
5376   else {
5377     return EXTR_BAD_PATH_SHAPE;
5378   }
5379
5380   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5381                           theHasRefPoint, theRefPoint, theMakeGroups);
5382 }
5383
5384
5385 //=======================================================================
5386 //function : MakeEdgePathPoints
5387 //purpose  : auxilary for ExtrusionAlongTrack
5388 //=======================================================================
5389 SMESH_MeshEditor::Extrusion_Error
5390 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5391                                      const TopoDS_Edge&                aTrackEdge,
5392                                      bool                              FirstIsStart,
5393                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5394 {
5395   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5396   aTolVec=1.e-7;
5397   aTolVec2=aTolVec*aTolVec;
5398   double aT1, aT2;
5399   TopoDS_Vertex aV1, aV2;
5400   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5401   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5402   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5403   // 2. Collect parameters on the track edge
5404   aPrms.push_front( aT1 );
5405   aPrms.push_back( aT2 );
5406   // sort parameters
5407   aPrms.sort();
5408   if( FirstIsStart ) {
5409     if ( aT1 > aT2 ) {
5410       aPrms.reverse();
5411     }
5412   }
5413   else {
5414     if ( aT2 > aT1 ) {
5415       aPrms.reverse();
5416     }
5417   }
5418   // 3. Path Points
5419   SMESH_MeshEditor_PathPoint aPP;
5420   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5421   std::list<double>::iterator aItD = aPrms.begin();
5422   for(; aItD != aPrms.end(); ++aItD) {
5423     double aT = *aItD;
5424     gp_Pnt aP3D;
5425     gp_Vec aVec;
5426     aC3D->D1( aT, aP3D, aVec );
5427     aL2 = aVec.SquareMagnitude();
5428     if ( aL2 < aTolVec2 )
5429       return EXTR_CANT_GET_TANGENT;
5430     gp_Dir aTgt( aVec );
5431     aPP.SetPnt( aP3D );
5432     aPP.SetTangent( aTgt );
5433     aPP.SetParameter( aT );
5434     LPP.push_back(aPP);
5435   }
5436   return EXTR_OK;
5437 }
5438
5439
5440 //=======================================================================
5441 //function : MakeExtrElements
5442 //purpose  : auxilary for ExtrusionAlongTrack
5443 //=======================================================================
5444 SMESH_MeshEditor::Extrusion_Error
5445 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5446                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5447                                    const bool                        theHasAngles,
5448                                    list<double>&                     theAngles,
5449                                    const bool                        theLinearVariation,
5450                                    const bool                        theHasRefPoint,
5451                                    const gp_Pnt&                     theRefPoint,
5452                                    const bool                        theMakeGroups)
5453 {
5454   const int aNbTP = fullList.size();
5455   // Angles
5456   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5457     LinearAngleVariation(aNbTP-1, theAngles);
5458   // fill vector of path points with angles
5459   vector<SMESH_MeshEditor_PathPoint> aPPs;
5460   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5461   list<double>::iterator                 itAngles = theAngles.begin();
5462   aPPs.push_back( *itPP++ );
5463   for( ; itPP != fullList.end(); itPP++) {
5464     aPPs.push_back( *itPP );
5465     if ( theHasAngles && itAngles != theAngles.end() )
5466       aPPs.back().SetAngle( *itAngles );
5467   }
5468
5469   TNodeOfNodeListMap   mapNewNodes;
5470   TElemOfVecOfNnlmiMap mapElemNewNodes;
5471   TElemOfElemListMap   newElemsMap;
5472   TIDSortedElemSet::iterator itElem;
5473   // source elements for each generated one
5474   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5475
5476   // 3. Center of rotation aV0
5477   gp_Pnt aV0 = theRefPoint;
5478   if ( !theHasRefPoint )
5479   {
5480     gp_XYZ aGC( 0.,0.,0. );
5481     TIDSortedElemSet newNodes;
5482
5483     itElem = theElements.begin();
5484     for ( ; itElem != theElements.end(); itElem++ ) {
5485       const SMDS_MeshElement* elem = *itElem;
5486
5487       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5488       while ( itN->more() ) {
5489         const SMDS_MeshElement* node = itN->next();
5490         if ( newNodes.insert( node ).second )
5491           aGC += SMESH_TNodeXYZ( node );
5492       }
5493     }
5494     aGC /= newNodes.size();
5495     aV0.SetXYZ( aGC );
5496   } // if (!theHasRefPoint) {
5497
5498   // 4. Processing the elements
5499   SMESHDS_Mesh* aMesh = GetMeshDS();
5500
5501   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5502     // check element type
5503     const SMDS_MeshElement* elem = *itElem;
5504     SMDSAbs_ElementType   aTypeE = elem->GetType();
5505     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5506       continue;
5507
5508     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5509     newNodesItVec.reserve( elem->NbNodes() );
5510
5511     // loop on elem nodes
5512     int nodeIndex = -1;
5513     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5514     while ( itN->more() )
5515     {
5516       ++nodeIndex;
5517       // check if a node has been already processed
5518       const SMDS_MeshNode* node =
5519         static_cast<const SMDS_MeshNode*>( itN->next() );
5520       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5521       if ( nIt == mapNewNodes.end() ) {
5522         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5523         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5524
5525         // make new nodes
5526         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5527         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5528         gp_Ax1 anAx1, anAxT1T0;
5529         gp_Dir aDT1x, aDT0x, aDT1T0;
5530
5531         aTolAng=1.e-4;
5532
5533         aV0x = aV0;
5534         aPN0 = SMESH_TNodeXYZ( node );
5535
5536         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5537         aP0x = aPP0.Pnt();
5538         aDT0x= aPP0.Tangent();
5539         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5540
5541         for ( int j = 1; j < aNbTP; ++j ) {
5542           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5543           aP1x     = aPP1.Pnt();
5544           aDT1x    = aPP1.Tangent();
5545           aAngle1x = aPP1.Angle();
5546
5547           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5548           // Translation
5549           gp_Vec aV01x( aP0x, aP1x );
5550           aTrsf.SetTranslation( aV01x );
5551
5552           // traslated point
5553           aV1x = aV0x.Transformed( aTrsf );
5554           aPN1 = aPN0.Transformed( aTrsf );
5555
5556           // rotation 1 [ T1,T0 ]
5557           aAngleT1T0=-aDT1x.Angle( aDT0x );
5558           if (fabs(aAngleT1T0) > aTolAng) {
5559             aDT1T0=aDT1x^aDT0x;
5560             anAxT1T0.SetLocation( aV1x );
5561             anAxT1T0.SetDirection( aDT1T0 );
5562             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5563
5564             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5565           }
5566
5567           // rotation 2
5568           if ( theHasAngles ) {
5569             anAx1.SetLocation( aV1x );
5570             anAx1.SetDirection( aDT1x );
5571             aTrsfRot.SetRotation( anAx1, aAngle1x );
5572
5573             aPN1 = aPN1.Transformed( aTrsfRot );
5574           }
5575
5576           // make new node
5577           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5578           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5579             // create additional node
5580             double x = ( aPN1.X() + aPN0.X() )/2.;
5581             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5582             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5583             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5584             myLastCreatedNodes.Append(newNode);
5585             srcNodes.Append( node );
5586             listNewNodes.push_back( newNode );
5587           }
5588           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5589           myLastCreatedNodes.Append(newNode);
5590           srcNodes.Append( node );
5591           listNewNodes.push_back( newNode );
5592
5593           aPN0 = aPN1;
5594           aP0x = aP1x;
5595           aV0x = aV1x;
5596           aDT0x = aDT1x;
5597         }
5598       }
5599
5600       else {
5601         // if current elem is quadratic and current node is not medium
5602         // we have to check - may be it is needed to insert additional nodes
5603         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5604           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5605           if(listNewNodes.size()==aNbTP-1) {
5606             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5607             gp_XYZ P(node->X(), node->Y(), node->Z());
5608             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5609             int i;
5610             for(i=0; i<aNbTP-1; i++) {
5611               const SMDS_MeshNode* N = *it;
5612               double x = ( N->X() + P.X() )/2.;
5613               double y = ( N->Y() + P.Y() )/2.;
5614               double z = ( N->Z() + P.Z() )/2.;
5615               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5616               srcNodes.Append( node );
5617               myLastCreatedNodes.Append(newN);
5618               aNodes[2*i] = newN;
5619               aNodes[2*i+1] = N;
5620               P = gp_XYZ(N->X(),N->Y(),N->Z());
5621             }
5622             listNewNodes.clear();
5623             for(i=0; i<2*(aNbTP-1); i++) {
5624               listNewNodes.push_back(aNodes[i]);
5625             }
5626           }
5627         }
5628       }
5629
5630       newNodesItVec.push_back( nIt );
5631     }
5632     // make new elements
5633     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5634     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5635     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5636   }
5637
5638   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5639
5640   if ( theMakeGroups )
5641     generateGroups( srcNodes, srcElems, "extruded");
5642
5643   return EXTR_OK;
5644 }
5645
5646
5647 //=======================================================================
5648 //function : LinearAngleVariation
5649 //purpose  : auxilary for ExtrusionAlongTrack
5650 //=======================================================================
5651 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5652                                             list<double>& Angles)
5653 {
5654   int nbAngles = Angles.size();
5655   if( nbSteps > nbAngles ) {
5656     vector<double> theAngles(nbAngles);
5657     list<double>::iterator it = Angles.begin();
5658     int i = -1;
5659     for(; it!=Angles.end(); it++) {
5660       i++;
5661       theAngles[i] = (*it);
5662     }
5663     list<double> res;
5664     double rAn2St = double( nbAngles ) / double( nbSteps );
5665     double angPrev = 0, angle;
5666     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5667       double angCur = rAn2St * ( iSt+1 );
5668       double angCurFloor  = floor( angCur );
5669       double angPrevFloor = floor( angPrev );
5670       if ( angPrevFloor == angCurFloor )
5671         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5672       else {
5673         int iP = int( angPrevFloor );
5674         double angPrevCeil = ceil(angPrev);
5675         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5676
5677         int iC = int( angCurFloor );
5678         if ( iC < nbAngles )
5679           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5680
5681         iP = int( angPrevCeil );
5682         while ( iC-- > iP )
5683           angle += theAngles[ iC ];
5684       }
5685       res.push_back(angle);
5686       angPrev = angCur;
5687     }
5688     Angles.clear();
5689     it = res.begin();
5690     for(; it!=res.end(); it++)
5691       Angles.push_back( *it );
5692   }
5693 }
5694
5695
5696 //================================================================================
5697 /*!
5698  * \brief Move or copy theElements applying theTrsf to their nodes
5699  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5700  *  \param theTrsf - transformation to apply
5701  *  \param theCopy - if true, create translated copies of theElems
5702  *  \param theMakeGroups - if true and theCopy, create translated groups
5703  *  \param theTargetMesh - mesh to copy translated elements into
5704  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5705  */
5706 //================================================================================
5707
5708 SMESH_MeshEditor::PGroupIDs
5709 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5710                              const gp_Trsf&     theTrsf,
5711                              const bool         theCopy,
5712                              const bool         theMakeGroups,
5713                              SMESH_Mesh*        theTargetMesh)
5714 {
5715   myLastCreatedElems.Clear();
5716   myLastCreatedNodes.Clear();
5717
5718   bool needReverse = false;
5719   string groupPostfix;
5720   switch ( theTrsf.Form() ) {
5721   case gp_PntMirror:
5722     MESSAGE("gp_PntMirror");
5723     needReverse = true;
5724     groupPostfix = "mirrored";
5725     break;
5726   case gp_Ax1Mirror:
5727     MESSAGE("gp_Ax1Mirror");
5728     groupPostfix = "mirrored";
5729     break;
5730   case gp_Ax2Mirror:
5731     MESSAGE("gp_Ax2Mirror");
5732     needReverse = true;
5733     groupPostfix = "mirrored";
5734     break;
5735   case gp_Rotation:
5736     MESSAGE("gp_Rotation");
5737     groupPostfix = "rotated";
5738     break;
5739   case gp_Translation:
5740     MESSAGE("gp_Translation");
5741     groupPostfix = "translated";
5742     break;
5743   case gp_Scale:
5744     MESSAGE("gp_Scale");
5745     groupPostfix = "scaled";
5746     break;
5747   case gp_CompoundTrsf: // different scale by axis
5748     MESSAGE("gp_CompoundTrsf");
5749     groupPostfix = "scaled";
5750     break;
5751   default:
5752     MESSAGE("default");
5753     needReverse = false;
5754     groupPostfix = "transformed";
5755   }
5756
5757   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5758   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5759   SMESHDS_Mesh* aMesh    = GetMeshDS();
5760
5761
5762   // map old node to new one
5763   TNodeNodeMap nodeMap;
5764
5765   // elements sharing moved nodes; those of them which have all
5766   // nodes mirrored but are not in theElems are to be reversed
5767   TIDSortedElemSet inverseElemSet;
5768
5769   // source elements for each generated one
5770   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5771
5772   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5773   TIDSortedElemSet orphanNode;
5774
5775   if ( theElems.empty() ) // transform the whole mesh
5776   {
5777     // add all elements
5778     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5779     while ( eIt->more() ) theElems.insert( eIt->next() );
5780     // add orphan nodes
5781     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5782     while ( nIt->more() )
5783     {
5784       const SMDS_MeshNode* node = nIt->next();
5785       if ( node->NbInverseElements() == 0)
5786         orphanNode.insert( node );
5787     }
5788   }
5789
5790   // loop on elements to transform nodes : first orphan nodes then elems
5791   TIDSortedElemSet::iterator itElem;
5792   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5793   for (int i=0; i<2; i++)
5794   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5795     const SMDS_MeshElement* elem = *itElem;
5796     if ( !elem )
5797       continue;
5798
5799     // loop on elem nodes
5800     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5801     while ( itN->more() ) {
5802
5803       const SMDS_MeshNode* node = cast2Node( itN->next() );
5804       // check if a node has been already transformed
5805       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5806         nodeMap.insert( make_pair ( node, node ));
5807       if ( !n2n_isnew.second )
5808         continue;
5809
5810       double coord[3];
5811       coord[0] = node->X();
5812       coord[1] = node->Y();
5813       coord[2] = node->Z();
5814       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5815       if ( theTargetMesh ) {
5816         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5817         n2n_isnew.first->second = newNode;
5818         myLastCreatedNodes.Append(newNode);
5819         srcNodes.Append( node );
5820       }
5821       else if ( theCopy ) {
5822         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5823         n2n_isnew.first->second = newNode;
5824         myLastCreatedNodes.Append(newNode);
5825         srcNodes.Append( node );
5826       }
5827       else {
5828         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5829         // node position on shape becomes invalid
5830         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5831           ( SMDS_SpacePosition::originSpacePosition() );
5832       }
5833
5834       // keep inverse elements
5835       if ( !theCopy && !theTargetMesh && needReverse ) {
5836         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5837         while ( invElemIt->more() ) {
5838           const SMDS_MeshElement* iel = invElemIt->next();
5839           inverseElemSet.insert( iel );
5840         }
5841       }
5842     }
5843   }
5844
5845   // either create new elements or reverse mirrored ones
5846   if ( !theCopy && !needReverse && !theTargetMesh )
5847     return PGroupIDs();
5848
5849   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5850   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5851     theElems.insert( *invElemIt );
5852
5853   // Replicate or reverse elements
5854
5855   std::vector<int> iForw;
5856   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5857   {
5858     const SMDS_MeshElement* elem = *itElem;
5859     if ( !elem ) continue;
5860
5861     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5862     int                  nbNodes  = elem->NbNodes();
5863     if ( geomType == SMDSGeom_NONE ) continue; // node
5864
5865     switch ( geomType ) {
5866
5867     case SMDSGeom_POLYGON:  // ---------------------- polygon
5868       {
5869         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5870         int iNode = 0;
5871         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5872         while (itN->more()) {
5873           const SMDS_MeshNode* node =
5874             static_cast<const SMDS_MeshNode*>(itN->next());
5875           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5876           if (nodeMapIt == nodeMap.end())
5877             break; // not all nodes transformed
5878           if (needReverse) {
5879             // reverse mirrored faces and volumes
5880             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5881           } else {
5882             poly_nodes[iNode] = (*nodeMapIt).second;
5883           }
5884           iNode++;
5885         }
5886         if ( iNode != nbNodes )
5887           continue; // not all nodes transformed
5888
5889         if ( theTargetMesh ) {
5890           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5891           srcElems.Append( elem );
5892         }
5893         else if ( theCopy ) {
5894           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5895           srcElems.Append( elem );
5896         }
5897         else {
5898           aMesh->ChangePolygonNodes(elem, poly_nodes);
5899         }
5900       }
5901       break;
5902
5903     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5904       {
5905         const SMDS_VtkVolume* aPolyedre =
5906           dynamic_cast<const SMDS_VtkVolume*>( elem );
5907         if (!aPolyedre) {
5908           MESSAGE("Warning: bad volumic element");
5909           continue;
5910         }
5911
5912         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5913         vector<int> quantities; quantities.reserve( nbNodes );
5914
5915         bool allTransformed = true;
5916         int nbFaces = aPolyedre->NbFaces();
5917         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5918           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5919           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5920             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5921             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5922             if (nodeMapIt == nodeMap.end()) {
5923               allTransformed = false; // not all nodes transformed
5924             } else {
5925               poly_nodes.push_back((*nodeMapIt).second);
5926             }
5927             if ( needReverse && allTransformed )
5928               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5929           }
5930           quantities.push_back(nbFaceNodes);
5931         }
5932         if ( !allTransformed )
5933           continue; // not all nodes transformed
5934
5935         if ( theTargetMesh ) {
5936           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5937           srcElems.Append( elem );
5938         }
5939         else if ( theCopy ) {
5940           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5941           srcElems.Append( elem );
5942         }
5943         else {
5944           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5945         }
5946       }
5947       break;
5948
5949     case SMDSGeom_BALL: // -------------------- Ball
5950       {
5951         if ( !theCopy && !theTargetMesh ) continue;
5952
5953         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5954         if (nodeMapIt == nodeMap.end())
5955           continue; // not all nodes transformed
5956
5957         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5958         if ( theTargetMesh ) {
5959           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5960           srcElems.Append( elem );
5961         }
5962         else {
5963           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5964           srcElems.Append( elem );
5965         }
5966       }
5967       break;
5968
5969     default: // ----------------------- Regular elements
5970
5971       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5972       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5973       const std::vector<int>& i = needReverse ? iRev : iForw;
5974
5975       // find transformed nodes
5976       vector<const SMDS_MeshNode*> nodes(nbNodes);
5977       int iNode = 0;
5978       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5979       while ( itN->more() ) {
5980         const SMDS_MeshNode* node =
5981           static_cast<const SMDS_MeshNode*>( itN->next() );
5982         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5983         if ( nodeMapIt == nodeMap.end() )
5984           break; // not all nodes transformed
5985         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5986       }
5987       if ( iNode != nbNodes )
5988         continue; // not all nodes transformed
5989
5990       if ( theTargetMesh ) {
5991         if ( SMDS_MeshElement* copy =
5992              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5993           myLastCreatedElems.Append( copy );
5994           srcElems.Append( elem );
5995         }
5996       }
5997       else if ( theCopy ) {
5998         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5999           srcElems.Append( elem );
6000       }
6001       else {
6002         // reverse element as it was reversed by transformation
6003         if ( nbNodes > 2 )
6004           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6005       }
6006     } // switch ( geomType )
6007
6008   } // loop on elements
6009
6010   PGroupIDs newGroupIDs;
6011
6012   if ( ( theMakeGroups && theCopy ) ||
6013        ( theMakeGroups && theTargetMesh ) )
6014     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6015
6016   return newGroupIDs;
6017 }
6018
6019 //=======================================================================
6020 /*!
6021  * \brief Create groups of elements made during transformation
6022  * \param nodeGens - nodes making corresponding myLastCreatedNodes
6023  * \param elemGens - elements making corresponding myLastCreatedElems
6024  * \param postfix - to append to names of new groups
6025  */
6026 //=======================================================================
6027
6028 SMESH_MeshEditor::PGroupIDs
6029 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6030                                  const SMESH_SequenceOfElemPtr& elemGens,
6031                                  const std::string&             postfix,
6032                                  SMESH_Mesh*                    targetMesh)
6033 {
6034   PGroupIDs newGroupIDs( new list<int> );
6035   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6036
6037   // Sort existing groups by types and collect their names
6038
6039   // to store an old group and a generated new ones
6040   using boost::tuple;
6041   using boost::make_tuple;
6042   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6043   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6044   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6045   // group names
6046   set< string > groupNames;
6047
6048   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6049   if ( !groupIt->more() ) return newGroupIDs;
6050
6051   int newGroupID = mesh->GetGroupIds().back()+1;
6052   while ( groupIt->more() )
6053   {
6054     SMESH_Group * group = groupIt->next();
6055     if ( !group ) continue;
6056     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6057     if ( !groupDS || groupDS->IsEmpty() ) continue;
6058     groupNames.insert    ( group->GetName() );
6059     groupDS->SetStoreName( group->GetName() );
6060     const SMDSAbs_ElementType type = groupDS->GetType();
6061     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6062     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6063     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6064     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6065   }
6066
6067   // Loop on nodes and elements to add them in new groups
6068
6069   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6070   {
6071     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6072     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6073     if ( gens.Length() != elems.Length() )
6074       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6075
6076     // loop on created elements
6077     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6078     {
6079       const SMDS_MeshElement* sourceElem = gens( iElem );
6080       if ( !sourceElem ) {
6081         MESSAGE("generateGroups(): NULL source element");
6082         continue;
6083       }
6084       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6085       if ( groupsOldNew.empty() ) { // no groups of this type at all
6086         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6087           ++iElem; // skip all elements made by sourceElem
6088         continue;
6089       }
6090       // collect all elements made by the iElem-th sourceElem
6091       list< const SMDS_MeshElement* > resultElems;
6092       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6093         if ( resElem != sourceElem )
6094           resultElems.push_back( resElem );
6095       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6096         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6097           if ( resElem != sourceElem )
6098             resultElems.push_back( resElem );
6099
6100       // there must be a top element
6101       const SMDS_MeshElement* topElem = 0;
6102       if ( isNodes )
6103       {
6104         topElem = resultElems.back();
6105         resultElems.pop_back();
6106       }
6107       else
6108       {
6109         list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6110         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6111           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6112           {
6113             topElem = *resElemIt;
6114             resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6115             break;
6116           }
6117       }
6118
6119       // add resultElems to groups originted from ones the sourceElem belongs to
6120       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6121       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6122       {
6123         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6124         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6125         {
6126           // fill in a new group
6127           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6128           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6129           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6130             newGroup.Add( *resElemIt );
6131
6132           // fill a "top" group
6133           if ( topElem )
6134           {
6135             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6136             newTopGroup.Add( topElem );
6137           }
6138         }
6139       }
6140     } // loop on created elements
6141   }// loop on nodes and elements
6142
6143   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6144
6145   list<int> topGrouIds;
6146   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6147   {
6148     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6149     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6150                                       orderedOldNewGroups[i]->get<2>() };
6151     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6152     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6153     {
6154       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6155       if ( newGroupDS->IsEmpty() )
6156       {
6157         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6158       }
6159       else
6160       {
6161         // set group type
6162         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6163
6164         // make a name
6165         const bool isTop = ( nbNewGroups == 2 &&
6166                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6167                              is2nd );
6168
6169         string name = oldGroupDS->GetStoreName();
6170         if ( !targetMesh ) {
6171           string suffix = ( isTop ? "top": postfix.c_str() );
6172           name += "_";
6173           name += suffix;
6174           int nb = 1;
6175           while ( !groupNames.insert( name ).second ) // name exists
6176             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6177         }
6178         else if ( isTop ) {
6179           name += "_top";
6180         }
6181         newGroupDS->SetStoreName( name.c_str() );
6182
6183         // make a SMESH_Groups
6184         mesh->AddGroup( newGroupDS );
6185         if ( isTop )
6186           topGrouIds.push_back( newGroupDS->GetID() );
6187         else
6188           newGroupIDs->push_back( newGroupDS->GetID() );
6189       }
6190     }
6191   }
6192   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6193
6194   return newGroupIDs;
6195 }
6196
6197 //================================================================================
6198 /*!
6199  * \brief Return list of group of nodes close to each other within theTolerance
6200  *        Search among theNodes or in the whole mesh if theNodes is empty using
6201  *        an Octree algorithm
6202  */
6203 //================================================================================
6204
6205 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6206                                             const double         theTolerance,
6207                                             TListOfListOfNodes & theGroupsOfNodes)
6208 {
6209   myLastCreatedElems.Clear();
6210   myLastCreatedNodes.Clear();
6211
6212   if ( theNodes.empty() )
6213   { // get all nodes in the mesh
6214     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6215     while ( nIt->more() )
6216       theNodes.insert( theNodes.end(),nIt->next());
6217   }
6218
6219   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6220 }
6221
6222 //=======================================================================
6223 //function : SimplifyFace
6224 //purpose  :
6225 //=======================================================================
6226
6227 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6228                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6229                                     vector<int>&                         quantities) const
6230 {
6231   int nbNodes = faceNodes.size();
6232
6233   if (nbNodes < 3)
6234     return 0;
6235
6236   set<const SMDS_MeshNode*> nodeSet;
6237
6238   // get simple seq of nodes
6239   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6240   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6241   int iSimple = 0, nbUnique = 0;
6242
6243   simpleNodes[iSimple++] = faceNodes[0];
6244   nbUnique++;
6245   for (int iCur = 1; iCur < nbNodes; iCur++) {
6246     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6247       simpleNodes[iSimple++] = faceNodes[iCur];
6248       if (nodeSet.insert( faceNodes[iCur] ).second)
6249         nbUnique++;
6250     }
6251   }
6252   int nbSimple = iSimple;
6253   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6254     nbSimple--;
6255     iSimple--;
6256   }
6257
6258   if (nbUnique < 3)
6259     return 0;
6260
6261   // separate loops
6262   int nbNew = 0;
6263   bool foundLoop = (nbSimple > nbUnique);
6264   while (foundLoop) {
6265     foundLoop = false;
6266     set<const SMDS_MeshNode*> loopSet;
6267     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6268       const SMDS_MeshNode* n = simpleNodes[iSimple];
6269       if (!loopSet.insert( n ).second) {
6270         foundLoop = true;
6271
6272         // separate loop
6273         int iC = 0, curLast = iSimple;
6274         for (; iC < curLast; iC++) {
6275           if (simpleNodes[iC] == n) break;
6276         }
6277         int loopLen = curLast - iC;
6278         if (loopLen > 2) {
6279           // create sub-element
6280           nbNew++;
6281           quantities.push_back(loopLen);
6282           for (; iC < curLast; iC++) {
6283             poly_nodes.push_back(simpleNodes[iC]);
6284           }
6285         }
6286         // shift the rest nodes (place from the first loop position)
6287         for (iC = curLast + 1; iC < nbSimple; iC++) {
6288           simpleNodes[iC - loopLen] = simpleNodes[iC];
6289         }
6290         nbSimple -= loopLen;
6291         iSimple -= loopLen;
6292       }
6293     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6294   } // while (foundLoop)
6295
6296   if (iSimple > 2) {
6297     nbNew++;
6298     quantities.push_back(iSimple);
6299     for (int i = 0; i < iSimple; i++)
6300       poly_nodes.push_back(simpleNodes[i]);
6301   }
6302
6303   return nbNew;
6304 }
6305
6306 //=======================================================================
6307 //function : MergeNodes
6308 //purpose  : In each group, the cdr of nodes are substituted by the first one
6309 //           in all elements.
6310 //=======================================================================
6311
6312 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6313 {
6314   MESSAGE("MergeNodes");
6315   myLastCreatedElems.Clear();
6316   myLastCreatedNodes.Clear();
6317
6318   SMESHDS_Mesh* aMesh = GetMeshDS();
6319
6320   TNodeNodeMap nodeNodeMap; // node to replace - new node
6321   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6322   list< int > rmElemIds, rmNodeIds;
6323
6324   // Fill nodeNodeMap and elems
6325
6326   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6327   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6328     list<const SMDS_MeshNode*>& nodes = *grIt;
6329     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6330     const SMDS_MeshNode* nToKeep = *nIt;
6331     //MESSAGE("node to keep " << nToKeep->GetID());
6332     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6333       const SMDS_MeshNode* nToRemove = *nIt;
6334       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6335       if ( nToRemove != nToKeep ) {
6336         //MESSAGE("  node to remove " << nToRemove->GetID());
6337         rmNodeIds.push_back( nToRemove->GetID() );
6338         AddToSameGroups( nToKeep, nToRemove, aMesh );
6339         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6340         // after MergeNodes() w/o creating node in place of merged ones.
6341         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6342         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6343           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6344             sm->SetIsAlwaysComputed( true );
6345       }
6346
6347       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6348       while ( invElemIt->more() ) {
6349         const SMDS_MeshElement* elem = invElemIt->next();
6350         elems.insert(elem);
6351       }
6352     }
6353   }
6354   // Change element nodes or remove an element
6355
6356   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6357   for ( ; eIt != elems.end(); eIt++ ) {
6358     const SMDS_MeshElement* elem = *eIt;
6359     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6360     int nbNodes = elem->NbNodes();
6361     int aShapeId = FindShape( elem );
6362
6363     set<const SMDS_MeshNode*> nodeSet;
6364     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6365     int iUnique = 0, iCur = 0, nbRepl = 0;
6366     vector<int> iRepl( nbNodes );
6367
6368     // get new seq of nodes
6369     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6370     while ( itN->more() ) {
6371       const SMDS_MeshNode* n =
6372         static_cast<const SMDS_MeshNode*>( itN->next() );
6373
6374       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6375       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6376         n = (*nnIt).second;
6377         // BUG 0020185: begin
6378         {
6379           bool stopRecur = false;
6380           set<const SMDS_MeshNode*> nodesRecur;
6381           nodesRecur.insert(n);
6382           while (!stopRecur) {
6383             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6384             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6385               n = (*nnIt_i).second;
6386               if (!nodesRecur.insert(n).second) {
6387                 // error: recursive dependancy
6388                 stopRecur = true;
6389               }
6390             }
6391             else
6392               stopRecur = true;
6393           }
6394         }
6395         // BUG 0020185: end
6396       }
6397       curNodes[ iCur ] = n;
6398       bool isUnique = nodeSet.insert( n ).second;
6399       if ( isUnique )
6400         uniqueNodes[ iUnique++ ] = n;
6401       else
6402         iRepl[ nbRepl++ ] = iCur;
6403       iCur++;
6404     }
6405
6406     // Analyse element topology after replacement
6407
6408     bool isOk = true;
6409     int nbUniqueNodes = nodeSet.size();
6410     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6411     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6412       // Polygons and Polyhedral volumes
6413       if (elem->IsPoly()) {
6414
6415         if (elem->GetType() == SMDSAbs_Face) {
6416           // Polygon
6417           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6418           int inode = 0;
6419           for (; inode < nbNodes; inode++) {
6420             face_nodes[inode] = curNodes[inode];
6421           }
6422
6423           vector<const SMDS_MeshNode *> polygons_nodes;
6424           vector<int> quantities;
6425           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6426           if (nbNew > 0) {
6427             inode = 0;
6428             for (int iface = 0; iface < nbNew; iface++) {
6429               int nbNodes = quantities[iface];
6430               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6431               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6432                 poly_nodes[ii] = polygons_nodes[inode];
6433               }
6434               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6435               myLastCreatedElems.Append(newElem);
6436               if (aShapeId)
6437                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6438             }
6439
6440             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6441             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6442             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6443             int quid =0;
6444             if (nbNew > 0) quid = nbNew - 1;
6445             vector<int> newquant(quantities.begin()+quid, quantities.end());
6446             const SMDS_MeshElement* newElem = 0;
6447             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6448             myLastCreatedElems.Append(newElem);
6449             if ( aShapeId && newElem )
6450               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6451             rmElemIds.push_back(elem->GetID());
6452           }
6453           else {
6454             rmElemIds.push_back(elem->GetID());
6455           }
6456
6457         }
6458         else if (elem->GetType() == SMDSAbs_Volume) {
6459           // Polyhedral volume
6460           if (nbUniqueNodes < 4) {
6461             rmElemIds.push_back(elem->GetID());
6462           }
6463           else {
6464             // each face has to be analyzed in order to check volume validity
6465             const SMDS_VtkVolume* aPolyedre =
6466               dynamic_cast<const SMDS_VtkVolume*>( elem );
6467             if (aPolyedre) {
6468               int nbFaces = aPolyedre->NbFaces();
6469
6470               vector<const SMDS_MeshNode *> poly_nodes;
6471               vector<int> quantities;
6472
6473               for (int iface = 1; iface <= nbFaces; iface++) {
6474                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6475                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6476
6477                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6478                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6479                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6480                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6481                     faceNode = (*nnIt).second;
6482                   }
6483                   faceNodes[inode - 1] = faceNode;
6484                 }
6485
6486                 SimplifyFace(faceNodes, poly_nodes, quantities);
6487               }
6488
6489               if (quantities.size() > 3) {
6490                 // to be done: remove coincident faces
6491               }
6492
6493               if (quantities.size() > 3)
6494                 {
6495                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6496                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6497                   const SMDS_MeshElement* newElem = 0;
6498                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6499                   myLastCreatedElems.Append(newElem);
6500                   if ( aShapeId && newElem )
6501                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6502                   rmElemIds.push_back(elem->GetID());
6503                 }
6504             }
6505             else {
6506               rmElemIds.push_back(elem->GetID());
6507             }
6508           }
6509         }
6510         else {
6511         }
6512
6513         continue;
6514       } // poly element
6515
6516       // Regular elements
6517       // TODO not all the possible cases are solved. Find something more generic?
6518       switch ( nbNodes ) {
6519       case 2: ///////////////////////////////////// EDGE
6520         isOk = false; break;
6521       case 3: ///////////////////////////////////// TRIANGLE
6522         isOk = false; break;
6523       case 4:
6524         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6525           isOk = false;
6526         else { //////////////////////////////////// QUADRANGLE
6527           if ( nbUniqueNodes < 3 )
6528             isOk = false;
6529           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6530             isOk = false; // opposite nodes stick
6531           //MESSAGE("isOk " << isOk);
6532         }
6533         break;
6534       case 6: ///////////////////////////////////// PENTAHEDRON
6535         if ( nbUniqueNodes == 4 ) {
6536           // ---------------------------------> tetrahedron
6537           if (nbRepl == 3 &&
6538               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6539             // all top nodes stick: reverse a bottom
6540             uniqueNodes[ 0 ] = curNodes [ 1 ];
6541             uniqueNodes[ 1 ] = curNodes [ 0 ];
6542           }
6543           else if (nbRepl == 3 &&
6544                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6545             // all bottom nodes stick: set a top before
6546             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6547             uniqueNodes[ 0 ] = curNodes [ 3 ];
6548             uniqueNodes[ 1 ] = curNodes [ 4 ];
6549             uniqueNodes[ 2 ] = curNodes [ 5 ];
6550           }
6551           else if (nbRepl == 4 &&
6552                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6553             // a lateral face turns into a line: reverse a bottom
6554             uniqueNodes[ 0 ] = curNodes [ 1 ];
6555             uniqueNodes[ 1 ] = curNodes [ 0 ];
6556           }
6557           else
6558             isOk = false;
6559         }
6560         else if ( nbUniqueNodes == 5 ) {
6561           // PENTAHEDRON --------------------> 2 tetrahedrons
6562           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6563             // a bottom node sticks with a linked top one
6564             // 1.
6565             SMDS_MeshElement* newElem =
6566               aMesh->AddVolume(curNodes[ 3 ],
6567                                curNodes[ 4 ],
6568                                curNodes[ 5 ],
6569                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6570             myLastCreatedElems.Append(newElem);
6571             if ( aShapeId )
6572               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6573             // 2. : reverse a bottom
6574             uniqueNodes[ 0 ] = curNodes [ 1 ];
6575             uniqueNodes[ 1 ] = curNodes [ 0 ];
6576             nbUniqueNodes = 4;
6577           }
6578           else
6579             isOk = false;
6580         }
6581         else
6582           isOk = false;
6583         break;
6584       case 8: {
6585         if(elem->IsQuadratic()) { // Quadratic quadrangle
6586           //   1    5    2
6587           //    +---+---+
6588           //    |       |
6589           //    |       |
6590           //   4+       +6
6591           //    |       |
6592           //    |       |
6593           //    +---+---+
6594           //   0    7    3
6595           isOk = false;
6596           if(nbRepl==2) {
6597             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
6598           }
6599           if(nbRepl==3) {
6600             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
6601             nbUniqueNodes = 6;
6602             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
6603               uniqueNodes[0] = curNodes[0];
6604               uniqueNodes[1] = curNodes[2];
6605               uniqueNodes[2] = curNodes[3];
6606               uniqueNodes[3] = curNodes[5];
6607               uniqueNodes[4] = curNodes[6];
6608               uniqueNodes[5] = curNodes[7];
6609               isOk = true;
6610             }
6611             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
6612               uniqueNodes[0] = curNodes[0];
6613               uniqueNodes[1] = curNodes[1];
6614               uniqueNodes[2] = curNodes[2];
6615               uniqueNodes[3] = curNodes[4];
6616               uniqueNodes[4] = curNodes[5];
6617               uniqueNodes[5] = curNodes[6];
6618               isOk = true;
6619             }
6620             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
6621               uniqueNodes[0] = curNodes[1];
6622               uniqueNodes[1] = curNodes[2];
6623               uniqueNodes[2] = curNodes[3];
6624               uniqueNodes[3] = curNodes[5];
6625               uniqueNodes[4] = curNodes[6];
6626               uniqueNodes[5] = curNodes[0];
6627               isOk = true;
6628             }
6629             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
6630               uniqueNodes[0] = curNodes[0];
6631               uniqueNodes[1] = curNodes[1];
6632               uniqueNodes[2] = curNodes[3];
6633               uniqueNodes[3] = curNodes[4];
6634               uniqueNodes[4] = curNodes[6];
6635               uniqueNodes[5] = curNodes[7];
6636               isOk = true;
6637             }
6638             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
6639               uniqueNodes[0] = curNodes[0];
6640               uniqueNodes[1] = curNodes[2];
6641               uniqueNodes[2] = curNodes[3];
6642               uniqueNodes[3] = curNodes[1];
6643               uniqueNodes[4] = curNodes[6];
6644               uniqueNodes[5] = curNodes[7];
6645               isOk = true;
6646             }
6647             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
6648               uniqueNodes[0] = curNodes[0];
6649               uniqueNodes[1] = curNodes[1];
6650               uniqueNodes[2] = curNodes[2];
6651               uniqueNodes[3] = curNodes[4];
6652               uniqueNodes[4] = curNodes[5];
6653               uniqueNodes[5] = curNodes[7];
6654               isOk = true;
6655             }
6656             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
6657               uniqueNodes[0] = curNodes[0];
6658               uniqueNodes[1] = curNodes[1];
6659               uniqueNodes[2] = curNodes[3];
6660               uniqueNodes[3] = curNodes[4];
6661               uniqueNodes[4] = curNodes[2];
6662               uniqueNodes[5] = curNodes[7];
6663               isOk = true;
6664             }
6665             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
6666               uniqueNodes[0] = curNodes[0];
6667               uniqueNodes[1] = curNodes[1];
6668               uniqueNodes[2] = curNodes[2];
6669               uniqueNodes[3] = curNodes[4];
6670               uniqueNodes[4] = curNodes[5];
6671               uniqueNodes[5] = curNodes[3];
6672               isOk = true;
6673             }
6674           }
6675           if(nbRepl==4) {
6676             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
6677           }
6678           if(nbRepl==5) {
6679             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
6680           }
6681           break;
6682         }
6683         //////////////////////////////////// HEXAHEDRON
6684         isOk = false;
6685         SMDS_VolumeTool hexa (elem);
6686         hexa.SetExternalNormal();
6687         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
6688           //////////////////////// HEX ---> 1 tetrahedron
6689           for ( int iFace = 0; iFace < 6; iFace++ ) {
6690             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6691             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6692                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6693                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6694               // one face turns into a point ...
6695               int iOppFace = hexa.GetOppFaceIndex( iFace );
6696               ind = hexa.GetFaceNodesIndices( iOppFace );
6697               int nbStick = 0;
6698               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
6699                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6700                   nbStick++;
6701               }
6702               if ( nbStick == 1 ) {
6703                 // ... and the opposite one - into a triangle.
6704                 // set a top node
6705                 ind = hexa.GetFaceNodesIndices( iFace );
6706                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
6707                 isOk = true;
6708               }
6709               break;
6710             }
6711           }
6712         }
6713         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
6714           //////////////////////// HEX ---> 1 prism
6715           int nbTria = 0, iTria[3];
6716           const int *ind; // indices of face nodes
6717           // look for triangular faces
6718           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
6719             ind = hexa.GetFaceNodesIndices( iFace );
6720             TIDSortedNodeSet faceNodes;
6721             for ( iCur = 0; iCur < 4; iCur++ )
6722               faceNodes.insert( curNodes[ind[iCur]] );
6723             if ( faceNodes.size() == 3 )
6724               iTria[ nbTria++ ] = iFace;
6725           }
6726           // check if triangles are opposite
6727           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
6728           {
6729             isOk = true;
6730             // set nodes of the bottom triangle
6731             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
6732             vector<int> indB;
6733             for ( iCur = 0; iCur < 4; iCur++ )
6734               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
6735                 indB.push_back( ind[iCur] );
6736             if ( !hexa.IsForward() )
6737               std::swap( indB[0], indB[2] );
6738             for ( iCur = 0; iCur < 3; iCur++ )
6739               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
6740             // set nodes of the top triangle
6741             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
6742             for ( iCur = 0; iCur < 3; ++iCur )
6743               for ( int j = 0; j < 4; ++j )
6744                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
6745                 {
6746                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
6747                   break;
6748                 }
6749           }
6750           break;
6751         }
6752         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
6753           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
6754           for ( int iFace = 0; iFace < 6; iFace++ ) {
6755             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6756             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6757                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6758                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6759               // one face turns into a point ...
6760               int iOppFace = hexa.GetOppFaceIndex( iFace );
6761               ind = hexa.GetFaceNodesIndices( iOppFace );
6762               int nbStick = 0;
6763               iUnique = 2;  // reverse a tetrahedron 1 bottom
6764               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
6765                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6766                   nbStick++;
6767                 else if ( iUnique >= 0 )
6768                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
6769               }
6770               if ( nbStick == 0 ) {
6771                 // ... and the opposite one is a quadrangle
6772                 // set a top node
6773                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
6774                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
6775                 nbUniqueNodes = 4;
6776                 // tetrahedron 2
6777                 SMDS_MeshElement* newElem =
6778                   aMesh->AddVolume(curNodes[ind[ 0 ]],
6779                                    curNodes[ind[ 3 ]],
6780                                    curNodes[ind[ 2 ]],
6781                                    curNodes[indTop[ 0 ]]);
6782                 myLastCreatedElems.Append(newElem);
6783                 if ( aShapeId )
6784                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
6785                 isOk = true;
6786               }
6787               break;
6788             }
6789           }
6790         }
6791         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
6792           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
6793           // find indices of quad and tri faces
6794           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
6795           for ( iFace = 0; iFace < 6; iFace++ ) {
6796             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6797             nodeSet.clear();
6798             for ( iCur = 0; iCur < 4; iCur++ )
6799               nodeSet.insert( curNodes[ind[ iCur ]] );
6800             nbUniqueNodes = nodeSet.size();
6801             if ( nbUniqueNodes == 3 )
6802               iTriFace[ nbTri++ ] = iFace;
6803             else if ( nbUniqueNodes == 4 )
6804               iQuadFace[ nbQuad++ ] = iFace;
6805           }
6806           if (nbQuad == 2 && nbTri == 4 &&
6807               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
6808             // 2 opposite quadrangles stuck with a diagonal;
6809             // sample groups of merged indices: (0-4)(2-6)
6810             // --------------------------------------------> 2 tetrahedrons
6811             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
6812             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
6813             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
6814             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
6815                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
6816               // stuck with 0-2 diagonal
6817               i0  = ind1[ 3 ];
6818               i1d = ind1[ 0 ];
6819               i2  = ind1[ 1 ];
6820               i3d = ind1[ 2 ];
6821               i0t = ind2[ 1 ];
6822               i2t = ind2[ 3 ];
6823             }
6824             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
6825                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
6826               // stuck with 1-3 diagonal
6827               i0  = ind1[ 0 ];
6828               i1d = ind1[ 1 ];
6829               i2  = ind1[ 2 ];
6830               i3d = ind1[ 3 ];
6831               i0t = ind2[ 0 ];
6832               i2t = ind2[ 1 ];
6833             }
6834             else {
6835               ASSERT(0);
6836             }
6837             // tetrahedron 1
6838             uniqueNodes[ 0 ] = curNodes [ i0 ];
6839             uniqueNodes[ 1 ] = curNodes [ i1d ];
6840             uniqueNodes[ 2 ] = curNodes [ i3d ];
6841             uniqueNodes[ 3 ] = curNodes [ i0t ];
6842             nbUniqueNodes = 4;
6843             // tetrahedron 2
6844             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
6845                                                          curNodes[ i2 ],
6846                                                          curNodes[ i3d ],
6847                                                          curNodes[ i2t ]);
6848             myLastCreatedElems.Append(newElem);
6849             if ( aShapeId )
6850               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6851             isOk = true;
6852           }
6853           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
6854                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
6855             // --------------------------------------------> prism
6856             // find 2 opposite triangles
6857             nbUniqueNodes = 6;
6858             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
6859               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
6860                 // find indices of kept and replaced nodes
6861                 // and fill unique nodes of 2 opposite triangles
6862                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
6863                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
6864                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
6865                 // fill unique nodes
6866                 iUnique = 0;
6867                 isOk = true;
6868                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
6869                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
6870                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
6871                   if ( n == nInit ) {
6872                     // iCur of a linked node of the opposite face (make normals co-directed):
6873                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
6874                     // check that correspondent corners of triangles are linked
6875                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
6876                       isOk = false;
6877                     else {
6878                       uniqueNodes[ iUnique ] = n;
6879                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
6880                       iUnique++;
6881                     }
6882                   }
6883                 }
6884                 break;
6885               }
6886             }
6887           }
6888         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
6889         else
6890         {
6891           MESSAGE("MergeNodes() removes hexahedron "<< elem);
6892         }
6893         break;
6894       } // HEXAHEDRON
6895
6896       default:
6897         isOk = false;
6898       } // switch ( nbNodes )
6899
6900     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
6901
6902     if ( isOk ) { // the elem remains valid after sticking nodes
6903       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
6904       {
6905         // Change nodes of polyedre
6906         const SMDS_VtkVolume* aPolyedre =
6907           dynamic_cast<const SMDS_VtkVolume*>( elem );
6908         if (aPolyedre) {
6909           int nbFaces = aPolyedre->NbFaces();
6910
6911           vector<const SMDS_MeshNode *> poly_nodes;
6912           vector<int> quantities (nbFaces);
6913
6914           for (int iface = 1; iface <= nbFaces; iface++) {
6915             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6916             quantities[iface - 1] = nbFaceNodes;
6917
6918             for (inode = 1; inode <= nbFaceNodes; inode++) {
6919               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
6920
6921               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
6922               if (nnIt != nodeNodeMap.end()) { // curNode sticks
6923                 curNode = (*nnIt).second;
6924               }
6925               poly_nodes.push_back(curNode);
6926             }
6927           }
6928           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
6929         }
6930       }
6931       else // replace non-polyhedron elements
6932       {
6933         const SMDSAbs_ElementType etyp = elem->GetType();
6934         const int elemId               = elem->GetID();
6935         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
6936         uniqueNodes.resize(nbUniqueNodes);
6937
6938         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
6939
6940         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6941         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
6942         if ( sm && newElem )
6943           sm->AddElement( newElem );
6944         if ( elem != newElem )
6945           ReplaceElemInGroups( elem, newElem, aMesh );
6946       }
6947     }
6948     else {
6949       // Remove invalid regular element or invalid polygon
6950       rmElemIds.push_back( elem->GetID() );
6951     }
6952
6953   } // loop on elements
6954
6955   // Remove bad elements, then equal nodes (order important)
6956
6957   Remove( rmElemIds, false );
6958   Remove( rmNodeIds, true );
6959
6960 }
6961
6962
6963 // ========================================================
6964 // class   : SortableElement
6965 // purpose : allow sorting elements basing on their nodes
6966 // ========================================================
6967 class SortableElement : public set <const SMDS_MeshElement*>
6968 {
6969 public:
6970
6971   SortableElement( const SMDS_MeshElement* theElem )
6972   {
6973     myElem = theElem;
6974     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
6975     while ( nodeIt->more() )
6976       this->insert( nodeIt->next() );
6977   }
6978
6979   const SMDS_MeshElement* Get() const
6980   { return myElem; }
6981
6982   void Set(const SMDS_MeshElement* e) const
6983   { myElem = e; }
6984
6985
6986 private:
6987   mutable const SMDS_MeshElement* myElem;
6988 };
6989
6990 //=======================================================================
6991 //function : FindEqualElements
6992 //purpose  : Return list of group of elements built on the same nodes.
6993 //           Search among theElements or in the whole mesh if theElements is empty
6994 //=======================================================================
6995
6996 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
6997                                          TListOfListOfElementsID & theGroupsOfElementsID)
6998 {
6999   myLastCreatedElems.Clear();
7000   myLastCreatedNodes.Clear();
7001
7002   typedef map< SortableElement, int > TMapOfNodeSet;
7003   typedef list<int> TGroupOfElems;
7004
7005   if ( theElements.empty() )
7006   { // get all elements in the mesh
7007     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7008     while ( eIt->more() )
7009       theElements.insert( theElements.end(), eIt->next());
7010   }
7011
7012   vector< TGroupOfElems > arrayOfGroups;
7013   TGroupOfElems groupOfElems;
7014   TMapOfNodeSet mapOfNodeSet;
7015
7016   TIDSortedElemSet::iterator elemIt = theElements.begin();
7017   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7018     const SMDS_MeshElement* curElem = *elemIt;
7019     SortableElement SE(curElem);
7020     int ind = -1;
7021     // check uniqueness
7022     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7023     if( !(pp.second) ) {
7024       TMapOfNodeSet::iterator& itSE = pp.first;
7025       ind = (*itSE).second;
7026       arrayOfGroups[ind].push_back(curElem->GetID());
7027     }
7028     else {
7029       groupOfElems.clear();
7030       groupOfElems.push_back(curElem->GetID());
7031       arrayOfGroups.push_back(groupOfElems);
7032       i++;
7033     }
7034   }
7035
7036   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7037   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7038     groupOfElems = *groupIt;
7039     if ( groupOfElems.size() > 1 ) {
7040       groupOfElems.sort();
7041       theGroupsOfElementsID.push_back(groupOfElems);
7042     }
7043   }
7044 }
7045
7046 //=======================================================================
7047 //function : MergeElements
7048 //purpose  : In each given group, substitute all elements by the first one.
7049 //=======================================================================
7050
7051 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7052 {
7053   myLastCreatedElems.Clear();
7054   myLastCreatedNodes.Clear();
7055
7056   typedef list<int> TListOfIDs;
7057   TListOfIDs rmElemIds; // IDs of elems to remove
7058
7059   SMESHDS_Mesh* aMesh = GetMeshDS();
7060
7061   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7062   while ( groupsIt != theGroupsOfElementsID.end() ) {
7063     TListOfIDs& aGroupOfElemID = *groupsIt;
7064     aGroupOfElemID.sort();
7065     int elemIDToKeep = aGroupOfElemID.front();
7066     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7067     aGroupOfElemID.pop_front();
7068     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7069     while ( idIt != aGroupOfElemID.end() ) {
7070       int elemIDToRemove = *idIt;
7071       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7072       // add the kept element in groups of removed one (PAL15188)
7073       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7074       rmElemIds.push_back( elemIDToRemove );
7075       ++idIt;
7076     }
7077     ++groupsIt;
7078   }
7079
7080   Remove( rmElemIds, false );
7081 }
7082
7083 //=======================================================================
7084 //function : MergeEqualElements
7085 //purpose  : Remove all but one of elements built on the same nodes.
7086 //=======================================================================
7087
7088 void SMESH_MeshEditor::MergeEqualElements()
7089 {
7090   TIDSortedElemSet aMeshElements; /* empty input ==
7091                                      to merge equal elements in the whole mesh */
7092   TListOfListOfElementsID aGroupsOfElementsID;
7093   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7094   MergeElements(aGroupsOfElementsID);
7095 }
7096
7097 //=======================================================================
7098 //function : findAdjacentFace
7099 //purpose  :
7100 //=======================================================================
7101
7102 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7103                                                 const SMDS_MeshNode* n2,
7104                                                 const SMDS_MeshElement* elem)
7105 {
7106   TIDSortedElemSet elemSet, avoidSet;
7107   if ( elem )
7108     avoidSet.insert ( elem );
7109   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7110 }
7111
7112 //=======================================================================
7113 //function : FindFreeBorder
7114 //purpose  :
7115 //=======================================================================
7116
7117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7118
7119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7120                                        const SMDS_MeshNode*             theSecondNode,
7121                                        const SMDS_MeshNode*             theLastNode,
7122                                        list< const SMDS_MeshNode* > &   theNodes,
7123                                        list< const SMDS_MeshElement* >& theFaces)
7124 {
7125   if ( !theFirstNode || !theSecondNode )
7126     return false;
7127   // find border face between theFirstNode and theSecondNode
7128   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7129   if ( !curElem )
7130     return false;
7131
7132   theFaces.push_back( curElem );
7133   theNodes.push_back( theFirstNode );
7134   theNodes.push_back( theSecondNode );
7135
7136   //vector<const SMDS_MeshNode*> nodes;
7137   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7138   TIDSortedElemSet foundElems;
7139   bool needTheLast = ( theLastNode != 0 );
7140
7141   while ( nStart != theLastNode ) {
7142     if ( nStart == theFirstNode )
7143       return !needTheLast;
7144
7145     // find all free border faces sharing form nStart
7146
7147     list< const SMDS_MeshElement* > curElemList;
7148     list< const SMDS_MeshNode* > nStartList;
7149     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7150     while ( invElemIt->more() ) {
7151       const SMDS_MeshElement* e = invElemIt->next();
7152       if ( e == curElem || foundElems.insert( e ).second ) {
7153         // get nodes
7154         int iNode = 0, nbNodes = e->NbNodes();
7155         //const SMDS_MeshNode* nodes[nbNodes+1];
7156         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7157
7158         if(e->IsQuadratic()) {
7159           const SMDS_VtkFace* F =
7160             dynamic_cast<const SMDS_VtkFace*>(e);
7161           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7162           // use special nodes iterator
7163           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7164           while( anIter->more() ) {
7165             nodes[ iNode++ ] = cast2Node(anIter->next());
7166           }
7167         }
7168         else {
7169           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7170           while ( nIt->more() )
7171             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7172         }
7173         nodes[ iNode ] = nodes[ 0 ];
7174         // check 2 links
7175         for ( iNode = 0; iNode < nbNodes; iNode++ )
7176           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7177                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7178               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7179           {
7180             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7181             curElemList.push_back( e );
7182           }
7183       }
7184     }
7185     // analyse the found
7186
7187     int nbNewBorders = curElemList.size();
7188     if ( nbNewBorders == 0 ) {
7189       // no free border furthermore
7190       return !needTheLast;
7191     }
7192     else if ( nbNewBorders == 1 ) {
7193       // one more element found
7194       nIgnore = nStart;
7195       nStart = nStartList.front();
7196       curElem = curElemList.front();
7197       theFaces.push_back( curElem );
7198       theNodes.push_back( nStart );
7199     }
7200     else {
7201       // several continuations found
7202       list< const SMDS_MeshElement* >::iterator curElemIt;
7203       list< const SMDS_MeshNode* >::iterator nStartIt;
7204       // check if one of them reached the last node
7205       if ( needTheLast ) {
7206         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7207              curElemIt!= curElemList.end();
7208              curElemIt++, nStartIt++ )
7209           if ( *nStartIt == theLastNode ) {
7210             theFaces.push_back( *curElemIt );
7211             theNodes.push_back( *nStartIt );
7212             return true;
7213           }
7214       }
7215       // find the best free border by the continuations
7216       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7217       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7218       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7219            curElemIt!= curElemList.end();
7220            curElemIt++, nStartIt++ )
7221       {
7222         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7223         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7224         // find one more free border
7225         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7226           cNL->clear();
7227           cFL->clear();
7228         }
7229         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7230           // choice: clear a worse one
7231           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7232           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7233           contNodes[ iWorse ].clear();
7234           contFaces[ iWorse ].clear();
7235         }
7236       }
7237       if ( contNodes[0].empty() && contNodes[1].empty() )
7238         return false;
7239
7240       // append the best free border
7241       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7242       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7243       theNodes.pop_back(); // remove nIgnore
7244       theNodes.pop_back(); // remove nStart
7245       theFaces.pop_back(); // remove curElem
7246       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7247       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7248       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7249       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7250       return true;
7251
7252     } // several continuations found
7253   } // while ( nStart != theLastNode )
7254
7255   return true;
7256 }
7257
7258 //=======================================================================
7259 //function : CheckFreeBorderNodes
7260 //purpose  : Return true if the tree nodes are on a free border
7261 //=======================================================================
7262
7263 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7264                                             const SMDS_MeshNode* theNode2,
7265                                             const SMDS_MeshNode* theNode3)
7266 {
7267   list< const SMDS_MeshNode* > nodes;
7268   list< const SMDS_MeshElement* > faces;
7269   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7270 }
7271
7272 //=======================================================================
7273 //function : SewFreeBorder
7274 //purpose  :
7275 //=======================================================================
7276
7277 SMESH_MeshEditor::Sew_Error
7278 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7279                                  const SMDS_MeshNode* theBordSecondNode,
7280                                  const SMDS_MeshNode* theBordLastNode,
7281                                  const SMDS_MeshNode* theSideFirstNode,
7282                                  const SMDS_MeshNode* theSideSecondNode,
7283                                  const SMDS_MeshNode* theSideThirdNode,
7284                                  const bool           theSideIsFreeBorder,
7285                                  const bool           toCreatePolygons,
7286                                  const bool           toCreatePolyedrs)
7287 {
7288   myLastCreatedElems.Clear();
7289   myLastCreatedNodes.Clear();
7290
7291   MESSAGE("::SewFreeBorder()");
7292   Sew_Error aResult = SEW_OK;
7293
7294   // ====================================
7295   //    find side nodes and elements
7296   // ====================================
7297
7298   list< const SMDS_MeshNode* > nSide[ 2 ];
7299   list< const SMDS_MeshElement* > eSide[ 2 ];
7300   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7301   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7302
7303   // Free border 1
7304   // --------------
7305   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7306                       nSide[0], eSide[0])) {
7307     MESSAGE(" Free Border 1 not found " );
7308     aResult = SEW_BORDER1_NOT_FOUND;
7309   }
7310   if (theSideIsFreeBorder) {
7311     // Free border 2
7312     // --------------
7313     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7314                         nSide[1], eSide[1])) {
7315       MESSAGE(" Free Border 2 not found " );
7316       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7317     }
7318   }
7319   if ( aResult != SEW_OK )
7320     return aResult;
7321
7322   if (!theSideIsFreeBorder) {
7323     // Side 2
7324     // --------------
7325
7326     // -------------------------------------------------------------------------
7327     // Algo:
7328     // 1. If nodes to merge are not coincident, move nodes of the free border
7329     //    from the coord sys defined by the direction from the first to last
7330     //    nodes of the border to the correspondent sys of the side 2
7331     // 2. On the side 2, find the links most co-directed with the correspondent
7332     //    links of the free border
7333     // -------------------------------------------------------------------------
7334
7335     // 1. Since sewing may break if there are volumes to split on the side 2,
7336     //    we wont move nodes but just compute new coordinates for them
7337     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7338     TNodeXYZMap nBordXYZ;
7339     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7340     list< const SMDS_MeshNode* >::iterator nBordIt;
7341
7342     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7343     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7344     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7345     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7346     double tol2 = 1.e-8;
7347     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7348     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7349       // Need node movement.
7350
7351       // find X and Z axes to create trsf
7352       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7353       gp_Vec X = Zs ^ Zb;
7354       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7355         // Zb || Zs
7356         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7357
7358       // coord systems
7359       gp_Ax3 toBordAx( Pb1, Zb, X );
7360       gp_Ax3 fromSideAx( Ps1, Zs, X );
7361       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7362       // set trsf
7363       gp_Trsf toBordSys, fromSide2Sys;
7364       toBordSys.SetTransformation( toBordAx );
7365       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7366       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7367
7368       // move
7369       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7370         const SMDS_MeshNode* n = *nBordIt;
7371         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7372         toBordSys.Transforms( xyz );
7373         fromSide2Sys.Transforms( xyz );
7374         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7375       }
7376     }
7377     else {
7378       // just insert nodes XYZ in the nBordXYZ map
7379       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7380         const SMDS_MeshNode* n = *nBordIt;
7381         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7382       }
7383     }
7384
7385     // 2. On the side 2, find the links most co-directed with the correspondent
7386     //    links of the free border
7387
7388     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7389     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7390     sideNodes.push_back( theSideFirstNode );
7391
7392     bool hasVolumes = false;
7393     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7394     set<long> foundSideLinkIDs, checkedLinkIDs;
7395     SMDS_VolumeTool volume;
7396     //const SMDS_MeshNode* faceNodes[ 4 ];
7397
7398     const SMDS_MeshNode*    sideNode;
7399     const SMDS_MeshElement* sideElem;
7400     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7401     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7402     nBordIt = bordNodes.begin();
7403     nBordIt++;
7404     // border node position and border link direction to compare with
7405     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7406     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7407     // choose next side node by link direction or by closeness to
7408     // the current border node:
7409     bool searchByDir = ( *nBordIt != theBordLastNode );
7410     do {
7411       // find the next node on the Side 2
7412       sideNode = 0;
7413       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7414       long linkID;
7415       checkedLinkIDs.clear();
7416       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7417
7418       // loop on inverse elements of current node (prevSideNode) on the Side 2
7419       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7420       while ( invElemIt->more() )
7421       {
7422         const SMDS_MeshElement* elem = invElemIt->next();
7423         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7424         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7425         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7426         bool isVolume = volume.Set( elem );
7427         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7428         if ( isVolume ) // --volume
7429           hasVolumes = true;
7430         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7431           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7432           if(elem->IsQuadratic()) {
7433             const SMDS_VtkFace* F =
7434               dynamic_cast<const SMDS_VtkFace*>(elem);
7435             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7436             // use special nodes iterator
7437             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7438             while( anIter->more() ) {
7439               nodes[ iNode ] = cast2Node(anIter->next());
7440               if ( nodes[ iNode++ ] == prevSideNode )
7441                 iPrevNode = iNode - 1;
7442             }
7443           }
7444           else {
7445             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7446             while ( nIt->more() ) {
7447               nodes[ iNode ] = cast2Node( nIt->next() );
7448               if ( nodes[ iNode++ ] == prevSideNode )
7449                 iPrevNode = iNode - 1;
7450             }
7451           }
7452           // there are 2 links to check
7453           nbNodes = 2;
7454         }
7455         else // --edge
7456           continue;
7457         // loop on links, to be precise, on the second node of links
7458         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7459           const SMDS_MeshNode* n = nodes[ iNode ];
7460           if ( isVolume ) {
7461             if ( !volume.IsLinked( n, prevSideNode ))
7462               continue;
7463           }
7464           else {
7465             if ( iNode ) // a node before prevSideNode
7466               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7467             else         // a node after prevSideNode
7468               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7469           }
7470           // check if this link was already used
7471           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7472           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7473           if (!isJustChecked &&
7474               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7475           {
7476             // test a link geometrically
7477             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7478             bool linkIsBetter = false;
7479             double dot = 0.0, dist = 0.0;
7480             if ( searchByDir ) { // choose most co-directed link
7481               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7482               linkIsBetter = ( dot > maxDot );
7483             }
7484             else { // choose link with the node closest to bordPos
7485               dist = ( nextXYZ - bordPos ).SquareModulus();
7486               linkIsBetter = ( dist < minDist );
7487             }
7488             if ( linkIsBetter ) {
7489               maxDot = dot;
7490               minDist = dist;
7491               linkID = iLink;
7492               sideNode = n;
7493               sideElem = elem;
7494             }
7495           }
7496         }
7497       } // loop on inverse elements of prevSideNode
7498
7499       if ( !sideNode ) {
7500         MESSAGE(" Cant find path by links of the Side 2 ");
7501         return SEW_BAD_SIDE_NODES;
7502       }
7503       sideNodes.push_back( sideNode );
7504       sideElems.push_back( sideElem );
7505       foundSideLinkIDs.insert ( linkID );
7506       prevSideNode = sideNode;
7507
7508       if ( *nBordIt == theBordLastNode )
7509         searchByDir = false;
7510       else {
7511         // find the next border link to compare with
7512         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7513         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7514         // move to next border node if sideNode is before forward border node (bordPos)
7515         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7516           prevBordNode = *nBordIt;
7517           nBordIt++;
7518           bordPos = nBordXYZ[ *nBordIt ];
7519           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7520           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7521         }
7522       }
7523     }
7524     while ( sideNode != theSideSecondNode );
7525
7526     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7527       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7528       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7529     }
7530   } // end nodes search on the side 2
7531
7532   // ============================
7533   // sew the border to the side 2
7534   // ============================
7535
7536   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7537   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7538
7539   TListOfListOfNodes nodeGroupsToMerge;
7540   if ( nbNodes[0] == nbNodes[1] ||
7541        ( theSideIsFreeBorder && !theSideThirdNode)) {
7542
7543     // all nodes are to be merged
7544
7545     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7546          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7547          nIt[0]++, nIt[1]++ )
7548     {
7549       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7550       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7551       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7552     }
7553   }
7554   else {
7555
7556     // insert new nodes into the border and the side to get equal nb of segments
7557
7558     // get normalized parameters of nodes on the borders
7559     //double param[ 2 ][ maxNbNodes ];
7560     double* param[ 2 ];
7561     param[0] = new double [ maxNbNodes ];
7562     param[1] = new double [ maxNbNodes ];
7563     int iNode, iBord;
7564     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7565       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7566       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7567       const SMDS_MeshNode* nPrev = *nIt;
7568       double bordLength = 0;
7569       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7570         const SMDS_MeshNode* nCur = *nIt;
7571         gp_XYZ segment (nCur->X() - nPrev->X(),
7572                         nCur->Y() - nPrev->Y(),
7573                         nCur->Z() - nPrev->Z());
7574         double segmentLen = segment.Modulus();
7575         bordLength += segmentLen;
7576         param[ iBord ][ iNode ] = bordLength;
7577         nPrev = nCur;
7578       }
7579       // normalize within [0,1]
7580       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7581         param[ iBord ][ iNode ] /= bordLength;
7582       }
7583     }
7584
7585     // loop on border segments
7586     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
7587     int i[ 2 ] = { 0, 0 };
7588     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
7589     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
7590
7591     TElemOfNodeListMap insertMap;
7592     TElemOfNodeListMap::iterator insertMapIt;
7593     // insertMap is
7594     // key:   elem to insert nodes into
7595     // value: 2 nodes to insert between + nodes to be inserted
7596     do {
7597       bool next[ 2 ] = { false, false };
7598
7599       // find min adjacent segment length after sewing
7600       double nextParam = 10., prevParam = 0;
7601       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7602         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
7603           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
7604         if ( i[ iBord ] > 0 )
7605           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
7606       }
7607       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7608       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7609       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
7610
7611       // choose to insert or to merge nodes
7612       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
7613       if ( Abs( du ) <= minSegLen * 0.2 ) {
7614         // merge
7615         // ------
7616         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7617         const SMDS_MeshNode* n0 = *nIt[0];
7618         const SMDS_MeshNode* n1 = *nIt[1];
7619         nodeGroupsToMerge.back().push_back( n1 );
7620         nodeGroupsToMerge.back().push_back( n0 );
7621         // position of node of the border changes due to merge
7622         param[ 0 ][ i[0] ] += du;
7623         // move n1 for the sake of elem shape evaluation during insertion.
7624         // n1 will be removed by MergeNodes() anyway
7625         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
7626         next[0] = next[1] = true;
7627       }
7628       else {
7629         // insert
7630         // ------
7631         int intoBord = ( du < 0 ) ? 0 : 1;
7632         const SMDS_MeshElement* elem = *eIt[ intoBord ];
7633         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
7634         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
7635         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
7636         if ( intoBord == 1 ) {
7637           // move node of the border to be on a link of elem of the side
7638           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
7639           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
7640           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
7641           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
7642           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
7643         }
7644         insertMapIt = insertMap.find( elem );
7645         bool notFound = ( insertMapIt == insertMap.end() );
7646         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
7647         if ( otherLink ) {
7648           // insert into another link of the same element:
7649           // 1. perform insertion into the other link of the elem
7650           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7651           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
7652           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
7653           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
7654           // 2. perform insertion into the link of adjacent faces
7655           while (true) {
7656             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
7657             if ( adjElem )
7658               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
7659             else
7660               break;
7661           }
7662           if (toCreatePolyedrs) {
7663             // perform insertion into the links of adjacent volumes
7664             UpdateVolumes(n12, n22, nodeList);
7665           }
7666           // 3. find an element appeared on n1 and n2 after the insertion
7667           insertMap.erase( elem );
7668           elem = findAdjacentFace( n1, n2, 0 );
7669         }
7670         if ( notFound || otherLink ) {
7671           // add element and nodes of the side into the insertMap
7672           insertMapIt = insertMap.insert
7673             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
7674           (*insertMapIt).second.push_back( n1 );
7675           (*insertMapIt).second.push_back( n2 );
7676         }
7677         // add node to be inserted into elem
7678         (*insertMapIt).second.push_back( nIns );
7679         next[ 1 - intoBord ] = true;
7680       }
7681
7682       // go to the next segment
7683       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7684         if ( next[ iBord ] ) {
7685           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
7686             eIt[ iBord ]++;
7687           nPrev[ iBord ] = *nIt[ iBord ];
7688           nIt[ iBord ]++; i[ iBord ]++;
7689         }
7690       }
7691     }
7692     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
7693
7694     // perform insertion of nodes into elements
7695
7696     for (insertMapIt = insertMap.begin();
7697          insertMapIt != insertMap.end();
7698          insertMapIt++ )
7699     {
7700       const SMDS_MeshElement* elem = (*insertMapIt).first;
7701       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7702       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
7703       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
7704
7705       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
7706
7707       if ( !theSideIsFreeBorder ) {
7708         // look for and insert nodes into the faces adjacent to elem
7709         while (true) {
7710           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
7711           if ( adjElem )
7712             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
7713           else
7714             break;
7715         }
7716       }
7717       if (toCreatePolyedrs) {
7718         // perform insertion into the links of adjacent volumes
7719         UpdateVolumes(n1, n2, nodeList);
7720       }
7721     }
7722
7723     delete param[0];
7724     delete param[1];
7725   } // end: insert new nodes
7726
7727   MergeNodes ( nodeGroupsToMerge );
7728
7729   return aResult;
7730 }
7731
7732 //=======================================================================
7733 //function : InsertNodesIntoLink
7734 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
7735 //           and theBetweenNode2 and split theElement
7736 //=======================================================================
7737
7738 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
7739                                            const SMDS_MeshNode*        theBetweenNode1,
7740                                            const SMDS_MeshNode*        theBetweenNode2,
7741                                            list<const SMDS_MeshNode*>& theNodesToInsert,
7742                                            const bool                  toCreatePoly)
7743 {
7744   if ( theFace->GetType() != SMDSAbs_Face ) return;
7745
7746   // find indices of 2 link nodes and of the rest nodes
7747   int iNode = 0, il1, il2, i3, i4;
7748   il1 = il2 = i3 = i4 = -1;
7749   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
7750   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
7751
7752   if(theFace->IsQuadratic()) {
7753     const SMDS_VtkFace* F =
7754       dynamic_cast<const SMDS_VtkFace*>(theFace);
7755     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7756     // use special nodes iterator
7757     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7758     while( anIter->more() ) {
7759       const SMDS_MeshNode* n = cast2Node(anIter->next());
7760       if ( n == theBetweenNode1 )
7761         il1 = iNode;
7762       else if ( n == theBetweenNode2 )
7763         il2 = iNode;
7764       else if ( i3 < 0 )
7765         i3 = iNode;
7766       else
7767         i4 = iNode;
7768       nodes[ iNode++ ] = n;
7769     }
7770   }
7771   else {
7772     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7773     while ( nodeIt->more() ) {
7774       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7775       if ( n == theBetweenNode1 )
7776         il1 = iNode;
7777       else if ( n == theBetweenNode2 )
7778         il2 = iNode;
7779       else if ( i3 < 0 )
7780         i3 = iNode;
7781       else
7782         i4 = iNode;
7783       nodes[ iNode++ ] = n;
7784     }
7785   }
7786   if ( il1 < 0 || il2 < 0 || i3 < 0 )
7787     return ;
7788
7789   // arrange link nodes to go one after another regarding the face orientation
7790   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
7791   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
7792   if ( reverse ) {
7793     iNode = il1;
7794     il1 = il2;
7795     il2 = iNode;
7796     aNodesToInsert.reverse();
7797   }
7798   // check that not link nodes of a quadrangles are in good order
7799   int nbFaceNodes = theFace->NbNodes();
7800   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
7801     iNode = i3;
7802     i3 = i4;
7803     i4 = iNode;
7804   }
7805
7806   if (toCreatePoly || theFace->IsPoly()) {
7807
7808     iNode = 0;
7809     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
7810
7811     // add nodes of face up to first node of link
7812     bool isFLN = false;
7813
7814     if(theFace->IsQuadratic()) {
7815       const SMDS_VtkFace* F =
7816         dynamic_cast<const SMDS_VtkFace*>(theFace);
7817       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7818       // use special nodes iterator
7819       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7820       while( anIter->more()  && !isFLN ) {
7821         const SMDS_MeshNode* n = cast2Node(anIter->next());
7822         poly_nodes[iNode++] = n;
7823         if (n == nodes[il1]) {
7824           isFLN = true;
7825         }
7826       }
7827       // add nodes to insert
7828       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7829       for (; nIt != aNodesToInsert.end(); nIt++) {
7830         poly_nodes[iNode++] = *nIt;
7831       }
7832       // add nodes of face starting from last node of link
7833       while ( anIter->more() ) {
7834         poly_nodes[iNode++] = cast2Node(anIter->next());
7835       }
7836     }
7837     else {
7838       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7839       while ( nodeIt->more() && !isFLN ) {
7840         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7841         poly_nodes[iNode++] = n;
7842         if (n == nodes[il1]) {
7843           isFLN = true;
7844         }
7845       }
7846       // add nodes to insert
7847       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7848       for (; nIt != aNodesToInsert.end(); nIt++) {
7849         poly_nodes[iNode++] = *nIt;
7850       }
7851       // add nodes of face starting from last node of link
7852       while ( nodeIt->more() ) {
7853         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7854         poly_nodes[iNode++] = n;
7855       }
7856     }
7857
7858     // edit or replace the face
7859     SMESHDS_Mesh *aMesh = GetMeshDS();
7860
7861     if (theFace->IsPoly()) {
7862       aMesh->ChangePolygonNodes(theFace, poly_nodes);
7863     }
7864     else {
7865       int aShapeId = FindShape( theFace );
7866
7867       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7868       myLastCreatedElems.Append(newElem);
7869       if ( aShapeId && newElem )
7870         aMesh->SetMeshElementOnShape( newElem, aShapeId );
7871
7872       aMesh->RemoveElement(theFace);
7873     }
7874     return;
7875   }
7876
7877   SMESHDS_Mesh *aMesh = GetMeshDS();
7878   if( !theFace->IsQuadratic() ) {
7879
7880     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
7881     int nbLinkNodes = 2 + aNodesToInsert.size();
7882     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
7883     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
7884     linkNodes[ 0 ] = nodes[ il1 ];
7885     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
7886     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7887     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7888       linkNodes[ iNode++ ] = *nIt;
7889     }
7890     // decide how to split a quadrangle: compare possible variants
7891     // and choose which of splits to be a quadrangle
7892     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
7893     if ( nbFaceNodes == 3 ) {
7894       iBestQuad = nbSplits;
7895       i4 = i3;
7896     }
7897     else if ( nbFaceNodes == 4 ) {
7898       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
7899       double aBestRate = DBL_MAX;
7900       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
7901         i1 = 0; i2 = 1;
7902         double aBadRate = 0;
7903         // evaluate elements quality
7904         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
7905           if ( iSplit == iQuad ) {
7906             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
7907                                    linkNodes[ i2++ ],
7908                                    nodes[ i3 ],
7909                                    nodes[ i4 ]);
7910             aBadRate += getBadRate( &quad, aCrit );
7911           }
7912           else {
7913             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
7914                                    linkNodes[ i2++ ],
7915                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
7916             aBadRate += getBadRate( &tria, aCrit );
7917           }
7918         }
7919         // choice
7920         if ( aBadRate < aBestRate ) {
7921           iBestQuad = iQuad;
7922           aBestRate = aBadRate;
7923         }
7924       }
7925     }
7926
7927     // create new elements
7928     int aShapeId = FindShape( theFace );
7929
7930     i1 = 0; i2 = 1;
7931     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
7932       SMDS_MeshElement* newElem = 0;
7933       if ( iSplit == iBestQuad )
7934         newElem = aMesh->AddFace (linkNodes[ i1++ ],
7935                                   linkNodes[ i2++ ],
7936                                   nodes[ i3 ],
7937                                   nodes[ i4 ]);
7938       else
7939         newElem = aMesh->AddFace (linkNodes[ i1++ ],
7940                                   linkNodes[ i2++ ],
7941                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
7942       myLastCreatedElems.Append(newElem);
7943       if ( aShapeId && newElem )
7944         aMesh->SetMeshElementOnShape( newElem, aShapeId );
7945     }
7946
7947     // change nodes of theFace
7948     const SMDS_MeshNode* newNodes[ 4 ];
7949     newNodes[ 0 ] = linkNodes[ i1 ];
7950     newNodes[ 1 ] = linkNodes[ i2 ];
7951     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
7952     newNodes[ 3 ] = nodes[ i4 ];
7953     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
7954     const SMDS_MeshElement* newElem = 0;
7955     if (iSplit == iBestQuad)
7956       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
7957     else
7958       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
7959     myLastCreatedElems.Append(newElem);
7960     if ( aShapeId && newElem )
7961       aMesh->SetMeshElementOnShape( newElem, aShapeId );
7962 } // end if(!theFace->IsQuadratic())
7963   else { // theFace is quadratic
7964     // we have to split theFace on simple triangles and one simple quadrangle
7965     int tmp = il1/2;
7966     int nbshift = tmp*2;
7967     // shift nodes in nodes[] by nbshift
7968     int i,j;
7969     for(i=0; i<nbshift; i++) {
7970       const SMDS_MeshNode* n = nodes[0];
7971       for(j=0; j<nbFaceNodes-1; j++) {
7972         nodes[j] = nodes[j+1];
7973       }
7974       nodes[nbFaceNodes-1] = n;
7975     }
7976     il1 = il1 - nbshift;
7977     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
7978     //   n0      n1     n2    n0      n1     n2
7979     //     +-----+-----+        +-----+-----+
7980     //      \         /         |           |
7981     //       \       /          |           |
7982     //      n5+     +n3       n7+           +n3
7983     //         \   /            |           |
7984     //          \ /             |           |
7985     //           +              +-----+-----+
7986     //           n4           n6      n5     n4
7987
7988     // create new elements
7989     int aShapeId = FindShape( theFace );
7990
7991     int n1,n2,n3;
7992     if(nbFaceNodes==6) { // quadratic triangle
7993       SMDS_MeshElement* newElem =
7994         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
7995       myLastCreatedElems.Append(newElem);
7996       if ( aShapeId && newElem )
7997         aMesh->SetMeshElementOnShape( newElem, aShapeId );
7998       if(theFace->IsMediumNode(nodes[il1])) {
7999         // create quadrangle
8000         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8001         myLastCreatedElems.Append(newElem);
8002         if ( aShapeId && newElem )
8003           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8004         n1 = 1;
8005         n2 = 2;
8006         n3 = 3;
8007       }
8008       else {
8009         // create quadrangle
8010         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8011         myLastCreatedElems.Append(newElem);
8012         if ( aShapeId && newElem )
8013           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8014         n1 = 0;
8015         n2 = 1;
8016         n3 = 5;
8017       }
8018     }
8019     else { // nbFaceNodes==8 - quadratic quadrangle
8020       SMDS_MeshElement* newElem =
8021         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8022       myLastCreatedElems.Append(newElem);
8023       if ( aShapeId && newElem )
8024         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8025       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8026       myLastCreatedElems.Append(newElem);
8027       if ( aShapeId && newElem )
8028         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8029       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8030       myLastCreatedElems.Append(newElem);
8031       if ( aShapeId && newElem )
8032         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8033       if(theFace->IsMediumNode(nodes[il1])) {
8034         // create quadrangle
8035         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8036         myLastCreatedElems.Append(newElem);
8037         if ( aShapeId && newElem )
8038           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8039         n1 = 1;
8040         n2 = 2;
8041         n3 = 3;
8042       }
8043       else {
8044         // create quadrangle
8045         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8046         myLastCreatedElems.Append(newElem);
8047         if ( aShapeId && newElem )
8048           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8049         n1 = 0;
8050         n2 = 1;
8051         n3 = 7;
8052       }
8053     }
8054     // create needed triangles using n1,n2,n3 and inserted nodes
8055     int nbn = 2 + aNodesToInsert.size();
8056     //const SMDS_MeshNode* aNodes[nbn];
8057     vector<const SMDS_MeshNode*> aNodes(nbn);
8058     aNodes[0] = nodes[n1];
8059     aNodes[nbn-1] = nodes[n2];
8060     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8061     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8062       aNodes[iNode++] = *nIt;
8063     }
8064     for(i=1; i<nbn; i++) {
8065       SMDS_MeshElement* newElem =
8066         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8067       myLastCreatedElems.Append(newElem);
8068       if ( aShapeId && newElem )
8069         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8070     }
8071   }
8072   // remove old face
8073   aMesh->RemoveElement(theFace);
8074 }
8075
8076 //=======================================================================
8077 //function : UpdateVolumes
8078 //purpose  :
8079 //=======================================================================
8080 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8081                                       const SMDS_MeshNode*        theBetweenNode2,
8082                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8083 {
8084   myLastCreatedElems.Clear();
8085   myLastCreatedNodes.Clear();
8086
8087   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8088   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8089     const SMDS_MeshElement* elem = invElemIt->next();
8090
8091     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8092     SMDS_VolumeTool aVolume (elem);
8093     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8094       continue;
8095
8096     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8097     int iface, nbFaces = aVolume.NbFaces();
8098     vector<const SMDS_MeshNode *> poly_nodes;
8099     vector<int> quantities (nbFaces);
8100
8101     for (iface = 0; iface < nbFaces; iface++) {
8102       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8103       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8104       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8105
8106       for (int inode = 0; inode < nbFaceNodes; inode++) {
8107         poly_nodes.push_back(faceNodes[inode]);
8108
8109         if (nbInserted == 0) {
8110           if (faceNodes[inode] == theBetweenNode1) {
8111             if (faceNodes[inode + 1] == theBetweenNode2) {
8112               nbInserted = theNodesToInsert.size();
8113
8114               // add nodes to insert
8115               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8116               for (; nIt != theNodesToInsert.end(); nIt++) {
8117                 poly_nodes.push_back(*nIt);
8118               }
8119             }
8120           }
8121           else if (faceNodes[inode] == theBetweenNode2) {
8122             if (faceNodes[inode + 1] == theBetweenNode1) {
8123               nbInserted = theNodesToInsert.size();
8124
8125               // add nodes to insert in reversed order
8126               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8127               nIt--;
8128               for (; nIt != theNodesToInsert.begin(); nIt--) {
8129                 poly_nodes.push_back(*nIt);
8130               }
8131               poly_nodes.push_back(*nIt);
8132             }
8133           }
8134           else {
8135           }
8136         }
8137       }
8138       quantities[iface] = nbFaceNodes + nbInserted;
8139     }
8140
8141     // Replace or update the volume
8142     SMESHDS_Mesh *aMesh = GetMeshDS();
8143
8144     if (elem->IsPoly()) {
8145       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8146
8147     }
8148     else {
8149       int aShapeId = FindShape( elem );
8150
8151       SMDS_MeshElement* newElem =
8152         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8153       myLastCreatedElems.Append(newElem);
8154       if (aShapeId && newElem)
8155         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8156
8157       aMesh->RemoveElement(elem);
8158     }
8159   }
8160 }
8161
8162 namespace
8163 {
8164   //================================================================================
8165   /*!
8166    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8167    */
8168   //================================================================================
8169
8170   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8171                            vector<const SMDS_MeshNode *> & nodes,
8172                            vector<int> &                   nbNodeInFaces )
8173   {
8174     nodes.clear();
8175     nbNodeInFaces.clear();
8176     SMDS_VolumeTool vTool ( elem );
8177     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8178     {
8179       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8180       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8181       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8182     }
8183   }
8184 }
8185
8186 //=======================================================================
8187 /*!
8188  * \brief Convert elements contained in a submesh to quadratic
8189  * \return int - nb of checked elements
8190  */
8191 //=======================================================================
8192
8193 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8194                                              SMESH_MesherHelper& theHelper,
8195                                              const bool          theForce3d)
8196 {
8197   int nbElem = 0;
8198   if( !theSm ) return nbElem;
8199
8200   vector<int> nbNodeInFaces;
8201   vector<const SMDS_MeshNode *> nodes;
8202   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8203   while(ElemItr->more())
8204   {
8205     nbElem++;
8206     const SMDS_MeshElement* elem = ElemItr->next();
8207     if( !elem ) continue;
8208
8209     // analyse a necessity of conversion
8210     const SMDSAbs_ElementType aType = elem->GetType();
8211     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8212       continue;
8213     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8214     bool hasCentralNodes = false;
8215     if ( elem->IsQuadratic() )
8216     {
8217       bool alreadyOK;
8218       switch ( aGeomType ) {
8219       case SMDSEntity_Quad_Triangle:
8220       case SMDSEntity_Quad_Quadrangle:
8221       case SMDSEntity_Quad_Hexa:
8222         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8223
8224       case SMDSEntity_BiQuad_Triangle:
8225       case SMDSEntity_BiQuad_Quadrangle:
8226       case SMDSEntity_TriQuad_Hexa:
8227         alreadyOK = theHelper.GetIsBiQuadratic();
8228         hasCentralNodes = true;
8229         break;
8230       default:
8231         alreadyOK = true;
8232       }
8233       // take into account already present modium nodes
8234       switch ( aType ) {
8235       case SMDSAbs_Volume:
8236         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8237       case SMDSAbs_Face:
8238         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8239       case SMDSAbs_Edge:
8240         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8241       default:;
8242       }
8243       if ( alreadyOK )
8244         continue;
8245     }
8246     // get elem data needed to re-create it
8247     //
8248     const int id      = elem->GetID();
8249     const int nbNodes = elem->NbCornerNodes();
8250     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8251     if ( aGeomType == SMDSEntity_Polyhedra )
8252       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8253     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8254       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8255
8256     // remove a linear element
8257     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8258
8259     // remove central nodes of biquadratic elements (biquad->quad convertion)
8260     if ( hasCentralNodes )
8261       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8262         if ( nodes[i]->NbInverseElements() == 0 )
8263           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8264
8265     const SMDS_MeshElement* NewElem = 0;
8266
8267     switch( aType )
8268     {
8269     case SMDSAbs_Edge :
8270       {
8271         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8272         break;
8273       }
8274     case SMDSAbs_Face :
8275       {
8276         switch(nbNodes)
8277         {
8278         case 3:
8279           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8280           break;
8281         case 4:
8282           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8283           break;
8284         default:
8285           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8286         }
8287         break;
8288       }
8289     case SMDSAbs_Volume :
8290       {
8291         switch( aGeomType )
8292         {
8293         case SMDSEntity_Tetra:
8294           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8295           break;
8296         case SMDSEntity_Pyramid:
8297           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8298           break;
8299         case SMDSEntity_Penta:
8300           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8301           break;
8302         case SMDSEntity_Hexa:
8303         case SMDSEntity_Quad_Hexa:
8304         case SMDSEntity_TriQuad_Hexa:
8305           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8306                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8307           break;
8308         case SMDSEntity_Hexagonal_Prism:
8309         default:
8310           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8311         }
8312         break;
8313       }
8314     default :
8315       continue;
8316     }
8317     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8318     if( NewElem && NewElem->getshapeId() < 1 )
8319       theSm->AddElement( NewElem );
8320   }
8321   return nbElem;
8322 }
8323 //=======================================================================
8324 //function : ConvertToQuadratic
8325 //purpose  :
8326 //=======================================================================
8327
8328 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8329 {
8330   SMESHDS_Mesh* meshDS = GetMeshDS();
8331
8332   SMESH_MesherHelper aHelper(*myMesh);
8333
8334   aHelper.SetIsQuadratic( true );
8335   aHelper.SetIsBiQuadratic( theToBiQuad );
8336   aHelper.SetElementsOnShape(true);
8337
8338   // convert elements assigned to sub-meshes
8339   int nbCheckedElems = 0;
8340   if ( myMesh->HasShapeToMesh() )
8341   {
8342     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8343     {
8344       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8345       while ( smIt->more() ) {
8346         SMESH_subMesh* sm = smIt->next();
8347         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8348           aHelper.SetSubShape( sm->GetSubShape() );
8349           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8350         }
8351       }
8352     }
8353   }
8354
8355   // convert elements NOT assigned to sub-meshes
8356   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8357   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8358   {
8359     aHelper.SetElementsOnShape(false);
8360     SMESHDS_SubMesh *smDS = 0;
8361
8362     // convert edges
8363     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8364     while( aEdgeItr->more() )
8365     {
8366       const SMDS_MeshEdge* edge = aEdgeItr->next();
8367       if ( !edge->IsQuadratic() )
8368       {
8369         int                  id = edge->GetID();
8370         const SMDS_MeshNode* n1 = edge->GetNode(0);
8371         const SMDS_MeshNode* n2 = edge->GetNode(1);
8372
8373         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8374
8375         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8376         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8377       }
8378       else
8379       {
8380         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8381       }
8382     }
8383
8384     // convert faces
8385     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8386     while( aFaceItr->more() )
8387     {
8388       const SMDS_MeshFace* face = aFaceItr->next();
8389       if ( !face ) continue;
8390       
8391       const SMDSAbs_EntityType type = face->GetEntityType();
8392       bool alreadyOK;
8393       switch( type )
8394       {
8395       case SMDSEntity_Quad_Triangle:
8396       case SMDSEntity_Quad_Quadrangle:
8397         alreadyOK = !theToBiQuad;
8398         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8399         break;
8400       case SMDSEntity_BiQuad_Triangle:
8401       case SMDSEntity_BiQuad_Quadrangle:
8402         alreadyOK = theToBiQuad;
8403         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8404         break;
8405       default: alreadyOK = false;
8406       }
8407       if ( alreadyOK )
8408         continue;
8409
8410       const int id = face->GetID();
8411       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8412
8413       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8414
8415       SMDS_MeshFace * NewFace = 0;
8416       switch( type )
8417       {
8418       case SMDSEntity_Triangle:
8419       case SMDSEntity_Quad_Triangle:
8420       case SMDSEntity_BiQuad_Triangle:
8421         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8422         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8423           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8424         break;
8425
8426       case SMDSEntity_Quadrangle:
8427       case SMDSEntity_Quad_Quadrangle:
8428       case SMDSEntity_BiQuad_Quadrangle:
8429         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8430         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8431           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8432         break;
8433
8434       default:;
8435         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8436       }
8437       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8438     }
8439
8440     // convert volumes
8441     vector<int> nbNodeInFaces;
8442     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8443     while(aVolumeItr->more())
8444     {
8445       const SMDS_MeshVolume* volume = aVolumeItr->next();
8446       if ( !volume ) continue;
8447
8448       const SMDSAbs_EntityType type = volume->GetEntityType();
8449       if (( theToBiQuad  && type == SMDSEntity_TriQuad_Hexa ) ||
8450           ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
8451       {
8452         aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8453         continue;
8454       }
8455       const int id = volume->GetID();
8456       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8457       if ( type == SMDSEntity_Polyhedra )
8458         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8459       else if ( type == SMDSEntity_Hexagonal_Prism )
8460         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8461
8462       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8463
8464       SMDS_MeshVolume * NewVolume = 0;
8465       switch ( type )
8466       {
8467       case SMDSEntity_Tetra:
8468         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8469         break;
8470       case SMDSEntity_Hexa:
8471       case SMDSEntity_Quad_Hexa:
8472       case SMDSEntity_TriQuad_Hexa:
8473         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8474                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8475         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8476           if ( nodes[i]->NbInverseElements() == 0 )
8477             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8478         break;
8479       case SMDSEntity_Pyramid:
8480         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8481                                       nodes[3], nodes[4], id, theForce3d);
8482         break;
8483       case SMDSEntity_Penta:
8484         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8485                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8486         break;
8487       case SMDSEntity_Hexagonal_Prism:
8488       default:
8489         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8490       }
8491       ReplaceElemInGroups(volume, NewVolume, meshDS);
8492     }
8493   }
8494
8495   if ( !theForce3d )
8496   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8497     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8498     // aHelper.FixQuadraticElements(myError);
8499     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8500   }
8501 }
8502
8503 //================================================================================
8504 /*!
8505  * \brief Makes given elements quadratic
8506  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8507  *  \param theElements - elements to make quadratic
8508  */
8509 //================================================================================
8510
8511 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8512                                           TIDSortedElemSet& theElements,
8513                                           const bool        theToBiQuad)
8514 {
8515   if ( theElements.empty() ) return;
8516
8517   // we believe that all theElements are of the same type
8518   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8519
8520   // get all nodes shared by theElements
8521   TIDSortedNodeSet allNodes;
8522   TIDSortedElemSet::iterator eIt = theElements.begin();
8523   for ( ; eIt != theElements.end(); ++eIt )
8524     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8525
8526   // complete theElements with elements of lower dim whose all nodes are in allNodes
8527
8528   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8529   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8530   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8531   for ( ; nIt != allNodes.end(); ++nIt )
8532   {
8533     const SMDS_MeshNode* n = *nIt;
8534     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8535     while ( invIt->more() )
8536     {
8537       const SMDS_MeshElement*      e = invIt->next();
8538       const SMDSAbs_ElementType type = e->GetType();
8539       if ( e->IsQuadratic() )
8540       {
8541         quadAdjacentElems[ type ].insert( e );
8542
8543         bool alreadyOK;
8544         switch ( e->GetEntityType() ) {
8545         case SMDSEntity_Quad_Triangle:
8546         case SMDSEntity_Quad_Quadrangle:
8547         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8548         case SMDSEntity_BiQuad_Triangle:
8549         case SMDSEntity_BiQuad_Quadrangle:
8550         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8551         default:                           alreadyOK = true;
8552         }
8553         if ( alreadyOK )
8554           continue;
8555       }
8556       if ( type >= elemType )
8557         continue; // same type or more complex linear element
8558
8559       if ( !checkedAdjacentElems[ type ].insert( e ).second )
8560         continue; // e is already checked
8561
8562       // check nodes
8563       bool allIn = true;
8564       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8565       while ( nodeIt->more() && allIn )
8566         allIn = allNodes.count( nodeIt->next() );
8567       if ( allIn )
8568         theElements.insert(e );
8569     }
8570   }
8571
8572   SMESH_MesherHelper helper(*myMesh);
8573   helper.SetIsQuadratic( true );
8574   helper.SetIsBiQuadratic( theToBiQuad );
8575
8576   // add links of quadratic adjacent elements to the helper
8577
8578   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
8579     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
8580           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
8581     {
8582       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
8583     }
8584   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
8585     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
8586           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
8587     {
8588       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
8589     }
8590   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
8591     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
8592           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
8593     {
8594       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
8595     }
8596
8597   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
8598
8599   SMESHDS_Mesh*  meshDS = GetMeshDS();
8600   SMESHDS_SubMesh* smDS = 0;
8601   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
8602   {
8603     const SMDS_MeshElement* elem = *eIt;
8604
8605     bool alreadyOK;
8606     int nbCentralNodes = 0;
8607     switch ( elem->GetEntityType() ) {
8608       // linear convertible
8609     case SMDSEntity_Edge:
8610     case SMDSEntity_Triangle:
8611     case SMDSEntity_Quadrangle:
8612     case SMDSEntity_Tetra:
8613     case SMDSEntity_Pyramid:
8614     case SMDSEntity_Hexa:
8615     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
8616       // quadratic that can become bi-quadratic
8617     case SMDSEntity_Quad_Triangle:
8618     case SMDSEntity_Quad_Quadrangle:
8619     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
8620       // bi-quadratic
8621     case SMDSEntity_BiQuad_Triangle:
8622     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
8623     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
8624       // the rest
8625     default:                           alreadyOK = true;
8626     }
8627     if ( alreadyOK ) continue;
8628
8629     const SMDSAbs_ElementType type = elem->GetType();
8630     const int                   id = elem->GetID();
8631     const int              nbNodes = elem->NbCornerNodes();
8632     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
8633
8634     helper.SetSubShape( elem->getshapeId() );
8635
8636     if ( !smDS || !smDS->Contains( elem ))
8637       smDS = meshDS->MeshElements( elem->getshapeId() );
8638     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
8639
8640     SMDS_MeshElement * newElem = 0;
8641     switch( nbNodes )
8642     {
8643     case 4: // cases for most frequently used element types go first (for optimization)
8644       if ( type == SMDSAbs_Volume )
8645         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8646       else
8647         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8648       break;
8649     case 8:
8650       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8651                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8652       break;
8653     case 3:
8654       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
8655       break;
8656     case 2:
8657       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8658       break;
8659     case 5:
8660       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8661                                  nodes[4], id, theForce3d);
8662       break;
8663     case 6:
8664       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8665                                  nodes[4], nodes[5], id, theForce3d);
8666       break;
8667     default:;
8668     }
8669     ReplaceElemInGroups( elem, newElem, meshDS);
8670     if( newElem && smDS )
8671       smDS->AddElement( newElem );
8672
8673      // remove central nodes
8674     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
8675       if ( nodes[i]->NbInverseElements() == 0 )
8676         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
8677
8678   } // loop on theElements
8679
8680   if ( !theForce3d )
8681   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8682     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8683     // helper.FixQuadraticElements( myError );
8684     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8685   }
8686 }
8687
8688 //=======================================================================
8689 /*!
8690  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8691  * \return int - nb of checked elements
8692  */
8693 //=======================================================================
8694
8695 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
8696                                      SMDS_ElemIteratorPtr theItr,
8697                                      const int            theShapeID)
8698 {
8699   int nbElem = 0;
8700   SMESHDS_Mesh* meshDS = GetMeshDS();
8701
8702   while( theItr->more() )
8703   {
8704     const SMDS_MeshElement* elem = theItr->next();
8705     nbElem++;
8706     if( elem && elem->IsQuadratic())
8707     {
8708       int id                    = elem->GetID();
8709       int nbCornerNodes         = elem->NbCornerNodes();
8710       SMDSAbs_ElementType aType = elem->GetType();
8711
8712       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
8713
8714       //remove a quadratic element
8715       if ( !theSm || !theSm->Contains( elem ))
8716         theSm = meshDS->MeshElements( elem->getshapeId() );
8717       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
8718
8719       // remove medium nodes
8720       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
8721         if ( nodes[i]->NbInverseElements() == 0 )
8722           meshDS->RemoveFreeNode( nodes[i], theSm );
8723
8724       // add a linear element
8725       nodes.resize( nbCornerNodes );
8726       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
8727       ReplaceElemInGroups(elem, newElem, meshDS);
8728       if( theSm && newElem )
8729         theSm->AddElement( newElem );
8730     }
8731   }
8732   return nbElem;
8733 }
8734
8735 //=======================================================================
8736 //function : ConvertFromQuadratic
8737 //purpose  :
8738 //=======================================================================
8739
8740 bool SMESH_MeshEditor::ConvertFromQuadratic()
8741 {
8742   int nbCheckedElems = 0;
8743   if ( myMesh->HasShapeToMesh() )
8744   {
8745     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8746     {
8747       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8748       while ( smIt->more() ) {
8749         SMESH_subMesh* sm = smIt->next();
8750         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8751           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8752       }
8753     }
8754   }
8755
8756   int totalNbElems =
8757     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8758   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8759   {
8760     SMESHDS_SubMesh *aSM = 0;
8761     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8762   }
8763
8764   return true;
8765 }
8766
8767 namespace
8768 {
8769   //================================================================================
8770   /*!
8771    * \brief Return true if all medium nodes of the element are in the node set
8772    */
8773   //================================================================================
8774
8775   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
8776   {
8777     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
8778       if ( !nodeSet.count( elem->GetNode(i) ))
8779         return false;
8780     return true;
8781   }
8782 }
8783
8784 //================================================================================
8785 /*!
8786  * \brief Makes given elements linear
8787  */
8788 //================================================================================
8789
8790 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
8791 {
8792   if ( theElements.empty() ) return;
8793
8794   // collect IDs of medium nodes of theElements; some of these nodes will be removed
8795   set<int> mediumNodeIDs;
8796   TIDSortedElemSet::iterator eIt = theElements.begin();
8797   for ( ; eIt != theElements.end(); ++eIt )
8798   {
8799     const SMDS_MeshElement* e = *eIt;
8800     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
8801       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
8802   }
8803
8804   // replace given elements by linear ones
8805   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
8806   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8807
8808   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
8809   // except those elements sharing medium nodes of quadratic element whose medium nodes
8810   // are not all in mediumNodeIDs
8811
8812   // get remaining medium nodes
8813   TIDSortedNodeSet mediumNodes;
8814   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
8815   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
8816     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
8817       mediumNodes.insert( mediumNodes.end(), n );
8818
8819   // find more quadratic elements to convert
8820   TIDSortedElemSet moreElemsToConvert;
8821   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
8822   for ( ; nIt != mediumNodes.end(); ++nIt )
8823   {
8824     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
8825     while ( invIt->more() )
8826     {
8827       const SMDS_MeshElement* e = invIt->next();
8828       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
8829       {
8830         // find a more complex element including e and
8831         // whose medium nodes are not in mediumNodes
8832         bool complexFound = false;
8833         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
8834         {
8835           SMDS_ElemIteratorPtr invIt2 =
8836             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
8837           while ( invIt2->more() )
8838           {
8839             const SMDS_MeshElement* eComplex = invIt2->next();
8840             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
8841             {
8842               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
8843               if ( nbCommonNodes == e->NbNodes())
8844               {
8845                 complexFound = true;
8846                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
8847                 break;
8848               }
8849             }
8850           }
8851         }
8852         if ( !complexFound )
8853           moreElemsToConvert.insert( e );
8854       }
8855     }
8856   }
8857   elemIt = elemSetIterator( moreElemsToConvert );
8858   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8859 }
8860
8861 //=======================================================================
8862 //function : SewSideElements
8863 //purpose  :
8864 //=======================================================================
8865
8866 SMESH_MeshEditor::Sew_Error
8867 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
8868                                    TIDSortedElemSet&    theSide2,
8869                                    const SMDS_MeshNode* theFirstNode1,
8870                                    const SMDS_MeshNode* theFirstNode2,
8871                                    const SMDS_MeshNode* theSecondNode1,
8872                                    const SMDS_MeshNode* theSecondNode2)
8873 {
8874   myLastCreatedElems.Clear();
8875   myLastCreatedNodes.Clear();
8876
8877   MESSAGE ("::::SewSideElements()");
8878   if ( theSide1.size() != theSide2.size() )
8879     return SEW_DIFF_NB_OF_ELEMENTS;
8880
8881   Sew_Error aResult = SEW_OK;
8882   // Algo:
8883   // 1. Build set of faces representing each side
8884   // 2. Find which nodes of the side 1 to merge with ones on the side 2
8885   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8886
8887   // =======================================================================
8888   // 1. Build set of faces representing each side:
8889   // =======================================================================
8890   // a. build set of nodes belonging to faces
8891   // b. complete set of faces: find missing faces whose nodes are in set of nodes
8892   // c. create temporary faces representing side of volumes if correspondent
8893   //    face does not exist
8894
8895   SMESHDS_Mesh* aMesh = GetMeshDS();
8896   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
8897   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
8898   TIDSortedElemSet             faceSet1, faceSet2;
8899   set<const SMDS_MeshElement*> volSet1,  volSet2;
8900   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
8901   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
8902   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
8903   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8904   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
8905   int iSide, iFace, iNode;
8906
8907   list<const SMDS_MeshElement* > tempFaceList;
8908   for ( iSide = 0; iSide < 2; iSide++ ) {
8909     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
8910     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
8911     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
8912     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
8913     set<const SMDS_MeshElement*>::iterator vIt;
8914     TIDSortedElemSet::iterator eIt;
8915     set<const SMDS_MeshNode*>::iterator    nIt;
8916
8917     // check that given nodes belong to given elements
8918     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8919     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8920     int firstIndex = -1, secondIndex = -1;
8921     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8922       const SMDS_MeshElement* elem = *eIt;
8923       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
8924       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8925       if ( firstIndex > -1 && secondIndex > -1 ) break;
8926     }
8927     if ( firstIndex < 0 || secondIndex < 0 ) {
8928       // we can simply return until temporary faces created
8929       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8930     }
8931
8932     // -----------------------------------------------------------
8933     // 1a. Collect nodes of existing faces
8934     //     and build set of face nodes in order to detect missing
8935     //     faces corresponding to sides of volumes
8936     // -----------------------------------------------------------
8937
8938     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8939
8940     // loop on the given element of a side
8941     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8942       //const SMDS_MeshElement* elem = *eIt;
8943       const SMDS_MeshElement* elem = *eIt;
8944       if ( elem->GetType() == SMDSAbs_Face ) {
8945         faceSet->insert( elem );
8946         set <const SMDS_MeshNode*> faceNodeSet;
8947         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8948         while ( nodeIt->more() ) {
8949           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8950           nodeSet->insert( n );
8951           faceNodeSet.insert( n );
8952         }
8953         setOfFaceNodeSet.insert( faceNodeSet );
8954       }
8955       else if ( elem->GetType() == SMDSAbs_Volume )
8956         volSet->insert( elem );
8957     }
8958     // ------------------------------------------------------------------------------
8959     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
8960     // ------------------------------------------------------------------------------
8961
8962     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
8963       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
8964       while ( fIt->more() ) { // loop on faces sharing a node
8965         const SMDS_MeshElement* f = fIt->next();
8966         if ( faceSet->find( f ) == faceSet->end() ) {
8967           // check if all nodes are in nodeSet and
8968           // complete setOfFaceNodeSet if they are
8969           set <const SMDS_MeshNode*> faceNodeSet;
8970           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
8971           bool allInSet = true;
8972           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
8973             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8974             if ( nodeSet->find( n ) == nodeSet->end() )
8975               allInSet = false;
8976             else
8977               faceNodeSet.insert( n );
8978           }
8979           if ( allInSet ) {
8980             faceSet->insert( f );
8981             setOfFaceNodeSet.insert( faceNodeSet );
8982           }
8983         }
8984       }
8985     }
8986
8987     // -------------------------------------------------------------------------
8988     // 1c. Create temporary faces representing sides of volumes if correspondent
8989     //     face does not exist
8990     // -------------------------------------------------------------------------
8991
8992     if ( !volSet->empty() ) {
8993       //int nodeSetSize = nodeSet->size();
8994
8995       // loop on given volumes
8996       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
8997         SMDS_VolumeTool vol (*vIt);
8998         // loop on volume faces: find free faces
8999         // --------------------------------------
9000         list<const SMDS_MeshElement* > freeFaceList;
9001         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9002           if ( !vol.IsFreeFace( iFace ))
9003             continue;
9004           // check if there is already a face with same nodes in a face set
9005           const SMDS_MeshElement* aFreeFace = 0;
9006           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9007           int nbNodes = vol.NbFaceNodes( iFace );
9008           set <const SMDS_MeshNode*> faceNodeSet;
9009           vol.GetFaceNodes( iFace, faceNodeSet );
9010           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9011           if ( isNewFace ) {
9012             // no such a face is given but it still can exist, check it
9013             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9014             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9015           }
9016           if ( !aFreeFace ) {
9017             // create a temporary face
9018             if ( nbNodes == 3 ) {
9019               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9020               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9021             }
9022             else if ( nbNodes == 4 ) {
9023               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9024               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9025             }
9026             else {
9027               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9028               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9029               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9030             }
9031             if ( aFreeFace )
9032               tempFaceList.push_back( aFreeFace );
9033           }
9034
9035           if ( aFreeFace )
9036             freeFaceList.push_back( aFreeFace );
9037
9038         } // loop on faces of a volume
9039
9040         // choose one of several free faces of a volume
9041         // --------------------------------------------
9042         if ( freeFaceList.size() > 1 ) {
9043           // choose a face having max nb of nodes shared by other elems of a side
9044           int maxNbNodes = -1;
9045           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9046           while ( fIt != freeFaceList.end() ) { // loop on free faces
9047             int nbSharedNodes = 0;
9048             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9049             while ( nodeIt->more() ) { // loop on free face nodes
9050               const SMDS_MeshNode* n =
9051                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9052               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9053               while ( invElemIt->more() ) {
9054                 const SMDS_MeshElement* e = invElemIt->next();
9055                 nbSharedNodes += faceSet->count( e );
9056                 nbSharedNodes += elemSet->count( e );
9057               }
9058             }
9059             if ( nbSharedNodes > maxNbNodes ) {
9060               maxNbNodes = nbSharedNodes;
9061               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9062             }
9063             else if ( nbSharedNodes == maxNbNodes ) {
9064               fIt++;
9065             }
9066             else {
9067               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9068             }
9069           }
9070           if ( freeFaceList.size() > 1 )
9071           {
9072             // could not choose one face, use another way
9073             // choose a face most close to the bary center of the opposite side
9074             gp_XYZ aBC( 0., 0., 0. );
9075             set <const SMDS_MeshNode*> addedNodes;
9076             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9077             eIt = elemSet2->begin();
9078             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9079               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9080               while ( nodeIt->more() ) { // loop on free face nodes
9081                 const SMDS_MeshNode* n =
9082                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9083                 if ( addedNodes.insert( n ).second )
9084                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9085               }
9086             }
9087             aBC /= addedNodes.size();
9088             double minDist = DBL_MAX;
9089             fIt = freeFaceList.begin();
9090             while ( fIt != freeFaceList.end() ) { // loop on free faces
9091               double dist = 0;
9092               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9093               while ( nodeIt->more() ) { // loop on free face nodes
9094                 const SMDS_MeshNode* n =
9095                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9096                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9097                 dist += ( aBC - p ).SquareModulus();
9098               }
9099               if ( dist < minDist ) {
9100                 minDist = dist;
9101                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9102               }
9103               else
9104                 fIt = freeFaceList.erase( fIt++ );
9105             }
9106           }
9107         } // choose one of several free faces of a volume
9108
9109         if ( freeFaceList.size() == 1 ) {
9110           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9111           faceSet->insert( aFreeFace );
9112           // complete a node set with nodes of a found free face
9113           //           for ( iNode = 0; iNode < ; iNode++ )
9114           //             nodeSet->insert( fNodes[ iNode ] );
9115         }
9116
9117       } // loop on volumes of a side
9118
9119       //       // complete a set of faces if new nodes in a nodeSet appeared
9120       //       // ----------------------------------------------------------
9121       //       if ( nodeSetSize != nodeSet->size() ) {
9122       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9123       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9124       //           while ( fIt->more() ) { // loop on faces sharing a node
9125       //             const SMDS_MeshElement* f = fIt->next();
9126       //             if ( faceSet->find( f ) == faceSet->end() ) {
9127       //               // check if all nodes are in nodeSet and
9128       //               // complete setOfFaceNodeSet if they are
9129       //               set <const SMDS_MeshNode*> faceNodeSet;
9130       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9131       //               bool allInSet = true;
9132       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9133       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9134       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9135       //                   allInSet = false;
9136       //                 else
9137       //                   faceNodeSet.insert( n );
9138       //               }
9139       //               if ( allInSet ) {
9140       //                 faceSet->insert( f );
9141       //                 setOfFaceNodeSet.insert( faceNodeSet );
9142       //               }
9143       //             }
9144       //           }
9145       //         }
9146       //       }
9147     } // Create temporary faces, if there are volumes given
9148   } // loop on sides
9149
9150   if ( faceSet1.size() != faceSet2.size() ) {
9151     // delete temporary faces: they are in reverseElements of actual nodes
9152 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9153 //    while ( tmpFaceIt->more() )
9154 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9155 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9156 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9157 //      aMesh->RemoveElement(*tmpFaceIt);
9158     MESSAGE("Diff nb of faces");
9159     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9160   }
9161
9162   // ============================================================
9163   // 2. Find nodes to merge:
9164   //              bind a node to remove to a node to put instead
9165   // ============================================================
9166
9167   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9168   if ( theFirstNode1 != theFirstNode2 )
9169     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9170   if ( theSecondNode1 != theSecondNode2 )
9171     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9172
9173   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9174   set< long > linkIdSet; // links to process
9175   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9176
9177   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9178   list< NLink > linkList[2];
9179   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9180   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9181   // loop on links in linkList; find faces by links and append links
9182   // of the found faces to linkList
9183   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9184   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9185   {
9186     NLink link[] = { *linkIt[0], *linkIt[1] };
9187     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9188     if ( !linkIdSet.count( linkID ) )
9189       continue;
9190
9191     // by links, find faces in the face sets,
9192     // and find indices of link nodes in the found faces;
9193     // in a face set, there is only one or no face sharing a link
9194     // ---------------------------------------------------------------
9195
9196     const SMDS_MeshElement* face[] = { 0, 0 };
9197     vector<const SMDS_MeshNode*> fnodes[2];
9198     int iLinkNode[2][2];
9199     TIDSortedElemSet avoidSet;
9200     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9201       const SMDS_MeshNode* n1 = link[iSide].first;
9202       const SMDS_MeshNode* n2 = link[iSide].second;
9203       //cout << "Side " << iSide << " ";
9204       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9205       // find a face by two link nodes
9206       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9207                                                       *faceSetPtr[ iSide ], avoidSet,
9208                                                       &iLinkNode[iSide][0],
9209                                                       &iLinkNode[iSide][1] );
9210       if ( face[ iSide ])
9211       {
9212         //cout << " F " << face[ iSide]->GetID() <<endl;
9213         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9214         // put face nodes to fnodes
9215         if ( face[ iSide ]->IsQuadratic() )
9216         {
9217           // use interlaced nodes iterator
9218           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9219           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9220           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9221           while ( nIter->more() )
9222             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9223         }
9224         else
9225         {
9226           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9227                                   face[ iSide ]->end_nodes() );
9228         }
9229         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9230       }
9231     }
9232
9233     // check similarity of elements of the sides
9234     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9235       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9236       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9237         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9238       }
9239       else {
9240         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9241       }
9242       break; // do not return because it's necessary to remove tmp faces
9243     }
9244
9245     // set nodes to merge
9246     // -------------------
9247
9248     if ( face[0] && face[1] )  {
9249       const int nbNodes = face[0]->NbNodes();
9250       if ( nbNodes != face[1]->NbNodes() ) {
9251         MESSAGE("Diff nb of face nodes");
9252         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9253         break; // do not return because it s necessary to remove tmp faces
9254       }
9255       bool reverse[] = { false, false }; // order of nodes in the link
9256       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9257         // analyse link orientation in faces
9258         int i1 = iLinkNode[ iSide ][ 0 ];
9259         int i2 = iLinkNode[ iSide ][ 1 ];
9260         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9261       }
9262       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9263       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9264       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9265       {
9266         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9267                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9268       }
9269
9270       // add other links of the faces to linkList
9271       // -----------------------------------------
9272
9273       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9274         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9275         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9276         if ( !iter_isnew.second ) { // already in a set: no need to process
9277           linkIdSet.erase( iter_isnew.first );
9278         }
9279         else // new in set == encountered for the first time: add
9280         {
9281           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9282           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9283           linkList[0].push_back ( NLink( n1, n2 ));
9284           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9285         }
9286       }
9287     } // 2 faces found
9288
9289     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9290       break;
9291
9292   } // loop on link lists
9293
9294   if ( aResult == SEW_OK &&
9295        ( //linkIt[0] != linkList[0].end() ||
9296          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9297     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9298              " " << (faceSetPtr[1]->empty()));
9299     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9300   }
9301
9302   // ====================================================================
9303   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9304   // ====================================================================
9305
9306   // delete temporary faces
9307 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9308 //  while ( tmpFaceIt->more() )
9309 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9310   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9311   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9312     aMesh->RemoveElement(*tmpFaceIt);
9313
9314   if ( aResult != SEW_OK)
9315     return aResult;
9316
9317   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9318   // loop on nodes replacement map
9319   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9320   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9321     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9322       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9323       nodeIDsToRemove.push_back( nToRemove->GetID() );
9324       // loop on elements sharing nToRemove
9325       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9326       while ( invElemIt->more() ) {
9327         const SMDS_MeshElement* e = invElemIt->next();
9328         // get a new suite of nodes: make replacement
9329         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9330         vector< const SMDS_MeshNode*> nodes( nbNodes );
9331         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9332         while ( nIt->more() ) {
9333           const SMDS_MeshNode* n =
9334             static_cast<const SMDS_MeshNode*>( nIt->next() );
9335           nnIt = nReplaceMap.find( n );
9336           if ( nnIt != nReplaceMap.end() ) {
9337             nbReplaced++;
9338             n = (*nnIt).second;
9339           }
9340           nodes[ i++ ] = n;
9341         }
9342         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9343         //         elemIDsToRemove.push_back( e->GetID() );
9344         //       else
9345         if ( nbReplaced )
9346           {
9347             SMDSAbs_ElementType etyp = e->GetType();
9348             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9349             if (newElem)
9350               {
9351                 myLastCreatedElems.Append(newElem);
9352                 AddToSameGroups(newElem, e, aMesh);
9353                 int aShapeId = e->getshapeId();
9354                 if ( aShapeId )
9355                   {
9356                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9357                   }
9358               }
9359             aMesh->RemoveElement(e);
9360           }
9361       }
9362     }
9363
9364   Remove( nodeIDsToRemove, true );
9365
9366   return aResult;
9367 }
9368
9369 //================================================================================
9370 /*!
9371  * \brief Find corresponding nodes in two sets of faces
9372  * \param theSide1 - first face set
9373  * \param theSide2 - second first face
9374  * \param theFirstNode1 - a boundary node of set 1
9375  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9376  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9377  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9378  * \param nReplaceMap - output map of corresponding nodes
9379  * \return bool  - is a success or not
9380  */
9381 //================================================================================
9382
9383 #ifdef _DEBUG_
9384 //#define DEBUG_MATCHING_NODES
9385 #endif
9386
9387 SMESH_MeshEditor::Sew_Error
9388 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9389                                     set<const SMDS_MeshElement*>& theSide2,
9390                                     const SMDS_MeshNode*          theFirstNode1,
9391                                     const SMDS_MeshNode*          theFirstNode2,
9392                                     const SMDS_MeshNode*          theSecondNode1,
9393                                     const SMDS_MeshNode*          theSecondNode2,
9394                                     TNodeNodeMap &                nReplaceMap)
9395 {
9396   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9397
9398   nReplaceMap.clear();
9399   if ( theFirstNode1 != theFirstNode2 )
9400     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9401   if ( theSecondNode1 != theSecondNode2 )
9402     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9403
9404   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9405   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9406
9407   list< NLink > linkList[2];
9408   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9409   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9410
9411   // loop on links in linkList; find faces by links and append links
9412   // of the found faces to linkList
9413   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9414   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9415     NLink link[] = { *linkIt[0], *linkIt[1] };
9416     if ( linkSet.find( link[0] ) == linkSet.end() )
9417       continue;
9418
9419     // by links, find faces in the face sets,
9420     // and find indices of link nodes in the found faces;
9421     // in a face set, there is only one or no face sharing a link
9422     // ---------------------------------------------------------------
9423
9424     const SMDS_MeshElement* face[] = { 0, 0 };
9425     list<const SMDS_MeshNode*> notLinkNodes[2];
9426     //bool reverse[] = { false, false }; // order of notLinkNodes
9427     int nbNodes[2];
9428     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9429     {
9430       const SMDS_MeshNode* n1 = link[iSide].first;
9431       const SMDS_MeshNode* n2 = link[iSide].second;
9432       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9433       set< const SMDS_MeshElement* > facesOfNode1;
9434       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9435       {
9436         // during a loop of the first node, we find all faces around n1,
9437         // during a loop of the second node, we find one face sharing both n1 and n2
9438         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9439         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9440         while ( fIt->more() ) { // loop on faces sharing a node
9441           const SMDS_MeshElement* f = fIt->next();
9442           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9443               ! facesOfNode1.insert( f ).second ) // f encounters twice
9444           {
9445             if ( face[ iSide ] ) {
9446               MESSAGE( "2 faces per link " );
9447               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9448             }
9449             face[ iSide ] = f;
9450             faceSet->erase( f );
9451
9452             // get not link nodes
9453             int nbN = f->NbNodes();
9454             if ( f->IsQuadratic() )
9455               nbN /= 2;
9456             nbNodes[ iSide ] = nbN;
9457             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9458             int i1 = f->GetNodeIndex( n1 );
9459             int i2 = f->GetNodeIndex( n2 );
9460             int iEnd = nbN, iBeg = -1, iDelta = 1;
9461             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9462             if ( reverse ) {
9463               std::swap( iEnd, iBeg ); iDelta = -1;
9464             }
9465             int i = i2;
9466             while ( true ) {
9467               i += iDelta;
9468               if ( i == iEnd ) i = iBeg + iDelta;
9469               if ( i == i1 ) break;
9470               nodes.push_back ( f->GetNode( i ) );
9471             }
9472           }
9473         }
9474       }
9475     }
9476     // check similarity of elements of the sides
9477     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9478       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9479       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9480         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9481       }
9482       else {
9483         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9484       }
9485     }
9486
9487     // set nodes to merge
9488     // -------------------
9489
9490     if ( face[0] && face[1] )  {
9491       if ( nbNodes[0] != nbNodes[1] ) {
9492         MESSAGE("Diff nb of face nodes");
9493         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9494       }
9495 #ifdef DEBUG_MATCHING_NODES
9496       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9497                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9498                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9499 #endif
9500       int nbN = nbNodes[0];
9501       {
9502         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9503         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9504         for ( int i = 0 ; i < nbN - 2; ++i ) {
9505 #ifdef DEBUG_MATCHING_NODES
9506           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9507 #endif
9508           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9509         }
9510       }
9511
9512       // add other links of the face 1 to linkList
9513       // -----------------------------------------
9514
9515       const SMDS_MeshElement* f0 = face[0];
9516       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9517       for ( int i = 0; i < nbN; i++ )
9518       {
9519         const SMDS_MeshNode* n2 = f0->GetNode( i );
9520         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9521           linkSet.insert( SMESH_TLink( n1, n2 ));
9522         if ( !iter_isnew.second ) { // already in a set: no need to process
9523           linkSet.erase( iter_isnew.first );
9524         }
9525         else // new in set == encountered for the first time: add
9526         {
9527 #ifdef DEBUG_MATCHING_NODES
9528           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9529                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9530 #endif
9531           linkList[0].push_back ( NLink( n1, n2 ));
9532           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9533         }
9534         n1 = n2;
9535       }
9536     } // 2 faces found
9537   } // loop on link lists
9538
9539   return SEW_OK;
9540 }
9541
9542 //================================================================================
9543 /*!
9544  * \brief Create elements equal (on same nodes) to given ones
9545  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9546  *              elements of the uppest dimension are duplicated.
9547  */
9548 //================================================================================
9549
9550 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9551 {
9552   CrearLastCreated();
9553   SMESHDS_Mesh* mesh = GetMeshDS();
9554
9555   // get an element type and an iterator over elements
9556
9557   SMDSAbs_ElementType type;
9558   SMDS_ElemIteratorPtr elemIt;
9559   vector< const SMDS_MeshElement* > allElems;
9560   if ( theElements.empty() )
9561   {
9562     if ( mesh->NbNodes() == 0 )
9563       return;
9564     // get most complex type
9565     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9566       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9567       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9568     };
9569     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9570       if ( mesh->GetMeshInfo().NbElements( types[i] ))
9571       {
9572         type = types[i];
9573         break;
9574       }
9575     // put all elements in the vector <allElems>
9576     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
9577     elemIt = mesh->elementsIterator( type );
9578     while ( elemIt->more() )
9579       allElems.push_back( elemIt->next());
9580     elemIt = elemSetIterator( allElems );
9581   }
9582   else
9583   {
9584     type = (*theElements.begin())->GetType();
9585     elemIt = elemSetIterator( theElements );
9586   }
9587
9588   // duplicate elements
9589
9590   if ( type == SMDSAbs_Ball )
9591   {
9592     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
9593     while ( elemIt->more() )
9594     {
9595       const SMDS_MeshElement* elem = elemIt->next();
9596       if ( elem->GetType() != SMDSAbs_Ball )
9597         continue;
9598       if (( elem = mesh->AddBall( elem->GetNode(0),
9599                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
9600         myLastCreatedElems.Append( elem );
9601     }
9602   }
9603   else
9604   {
9605     vector< const SMDS_MeshNode* > nodes;
9606     while ( elemIt->more() )
9607     {
9608       const SMDS_MeshElement* elem = elemIt->next();
9609       if ( elem->GetType() != type )
9610         continue;
9611
9612       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9613
9614       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
9615       {
9616         std::vector<int> quantities =
9617           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
9618         elem = mesh->AddPolyhedralVolume( nodes, quantities );
9619       }
9620       else
9621       {
9622         AddElement( nodes, type, elem->IsPoly() );
9623         elem = 0; // myLastCreatedElems is already filled
9624       }
9625       if ( elem )
9626         myLastCreatedElems.Append( elem );
9627     }
9628   }
9629 }
9630
9631 //================================================================================
9632 /*!
9633   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9634   \param theElems - the list of elements (edges or faces) to be replicated
9635   The nodes for duplication could be found from these elements
9636   \param theNodesNot - list of nodes to NOT replicate
9637   \param theAffectedElems - the list of elements (cells and edges) to which the
9638   replicated nodes should be associated to.
9639   \return TRUE if operation has been completed successfully, FALSE otherwise
9640 */
9641 //================================================================================
9642
9643 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9644                                     const TIDSortedElemSet& theNodesNot,
9645                                     const TIDSortedElemSet& theAffectedElems )
9646 {
9647   myLastCreatedElems.Clear();
9648   myLastCreatedNodes.Clear();
9649
9650   if ( theElems.size() == 0 )
9651     return false;
9652
9653   SMESHDS_Mesh* aMeshDS = GetMeshDS();
9654   if ( !aMeshDS )
9655     return false;
9656
9657   bool res = false;
9658   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9659   // duplicate elements and nodes
9660   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9661   // replce nodes by duplications
9662   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9663   return res;
9664 }
9665
9666 //================================================================================
9667 /*!
9668   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9669   \param theMeshDS - mesh instance
9670   \param theElems - the elements replicated or modified (nodes should be changed)
9671   \param theNodesNot - nodes to NOT replicate
9672   \param theNodeNodeMap - relation of old node to new created node
9673   \param theIsDoubleElem - flag os to replicate element or modify
9674   \return TRUE if operation has been completed successfully, FALSE otherwise
9675 */
9676 //================================================================================
9677
9678 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
9679                                     const TIDSortedElemSet& theElems,
9680                                     const TIDSortedElemSet& theNodesNot,
9681                                     std::map< const SMDS_MeshNode*,
9682                                     const SMDS_MeshNode* >& theNodeNodeMap,
9683                                     const bool theIsDoubleElem )
9684 {
9685   MESSAGE("doubleNodes");
9686   // iterate on through element and duplicate them (by nodes duplication)
9687   bool res = false;
9688   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9689   for ( ;  elemItr != theElems.end(); ++elemItr )
9690   {
9691     const SMDS_MeshElement* anElem = *elemItr;
9692     if (!anElem)
9693       continue;
9694
9695     bool isDuplicate = false;
9696     // duplicate nodes to duplicate element
9697     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9698     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9699     int ind = 0;
9700     while ( anIter->more() )
9701     {
9702
9703       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9704       SMDS_MeshNode* aNewNode = aCurrNode;
9705       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9706         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9707       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9708       {
9709         // duplicate node
9710         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9711         theNodeNodeMap[ aCurrNode ] = aNewNode;
9712         myLastCreatedNodes.Append( aNewNode );
9713       }
9714       isDuplicate |= (aCurrNode != aNewNode);
9715       newNodes[ ind++ ] = aNewNode;
9716     }
9717     if ( !isDuplicate )
9718       continue;
9719
9720     if ( theIsDoubleElem )
9721       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
9722     else
9723       {
9724       MESSAGE("ChangeElementNodes");
9725       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9726       }
9727     res = true;
9728   }
9729   return res;
9730 }
9731
9732 //================================================================================
9733 /*!
9734   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9735   \param theNodes - identifiers of nodes to be doubled
9736   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9737          nodes. If list of element identifiers is empty then nodes are doubled but
9738          they not assigned to elements
9739   \return TRUE if operation has been completed successfully, FALSE otherwise
9740 */
9741 //================================================================================
9742
9743 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9744                                     const std::list< int >& theListOfModifiedElems )
9745 {
9746   MESSAGE("DoubleNodes");
9747   myLastCreatedElems.Clear();
9748   myLastCreatedNodes.Clear();
9749
9750   if ( theListOfNodes.size() == 0 )
9751     return false;
9752
9753   SMESHDS_Mesh* aMeshDS = GetMeshDS();
9754   if ( !aMeshDS )
9755     return false;
9756
9757   // iterate through nodes and duplicate them
9758
9759   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9760
9761   std::list< int >::const_iterator aNodeIter;
9762   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9763   {
9764     int aCurr = *aNodeIter;
9765     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9766     if ( !aNode )
9767       continue;
9768
9769     // duplicate node
9770
9771     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9772     if ( aNewNode )
9773     {
9774       anOldNodeToNewNode[ aNode ] = aNewNode;
9775       myLastCreatedNodes.Append( aNewNode );
9776     }
9777   }
9778
9779   // Create map of new nodes for modified elements
9780
9781   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9782
9783   std::list< int >::const_iterator anElemIter;
9784   for ( anElemIter = theListOfModifiedElems.begin();
9785         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9786   {
9787     int aCurr = *anElemIter;
9788     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9789     if ( !anElem )
9790       continue;
9791
9792     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9793
9794     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9795     int ind = 0;
9796     while ( anIter->more() )
9797     {
9798       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9799       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9800       {
9801         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9802         aNodeArr[ ind++ ] = aNewNode;
9803       }
9804       else
9805         aNodeArr[ ind++ ] = aCurrNode;
9806     }
9807     anElemToNodes[ anElem ] = aNodeArr;
9808   }
9809
9810   // Change nodes of elements
9811
9812   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9813     anElemToNodesIter = anElemToNodes.begin();
9814   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9815   {
9816     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9817     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9818     if ( anElem )
9819       {
9820       MESSAGE("ChangeElementNodes");
9821       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9822       }
9823   }
9824
9825   return true;
9826 }
9827
9828 namespace {
9829
9830   //================================================================================
9831   /*!
9832   \brief Check if element located inside shape
9833   \return TRUE if IN or ON shape, FALSE otherwise
9834   */
9835   //================================================================================
9836
9837   template<class Classifier>
9838   bool isInside(const SMDS_MeshElement* theElem,
9839                 Classifier&             theClassifier,
9840                 const double            theTol)
9841   {
9842     gp_XYZ centerXYZ (0, 0, 0);
9843     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9844     while (aNodeItr->more())
9845       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
9846
9847     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9848     theClassifier.Perform(aPnt, theTol);
9849     TopAbs_State aState = theClassifier.State();
9850     return (aState == TopAbs_IN || aState == TopAbs_ON );
9851   }
9852
9853   //================================================================================
9854   /*!
9855    * \brief Classifier of the 3D point on the TopoDS_Face
9856    *        with interaface suitable for isInside()
9857    */
9858   //================================================================================
9859
9860   struct _FaceClassifier
9861   {
9862     Extrema_ExtPS       _extremum;
9863     BRepAdaptor_Surface _surface;
9864     TopAbs_State        _state;
9865
9866     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9867     {
9868       _extremum.Initialize( _surface,
9869                             _surface.FirstUParameter(), _surface.LastUParameter(),
9870                             _surface.FirstVParameter(), _surface.LastVParameter(),
9871                             _surface.Tolerance(), _surface.Tolerance() );
9872     }
9873     void Perform(const gp_Pnt& aPnt, double theTol)
9874     {
9875       _state = TopAbs_OUT;
9876       _extremum.Perform(aPnt);
9877       if ( _extremum.IsDone() )
9878         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9879 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
9880           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9881 #else
9882           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9883 #endif
9884     }
9885     TopAbs_State State() const
9886     {
9887       return _state;
9888     }
9889   };
9890 }
9891
9892 //================================================================================
9893 /*!
9894   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
9895   This method is the first step of DoubleNodeElemGroupsInRegion.
9896   \param theElems - list of groups of elements (edges or faces) to be replicated
9897   \param theNodesNot - list of groups of nodes not to replicated
9898   \param theShape - shape to detect affected elements (element which geometric center
9899          located on or inside shape).
9900          The replicated nodes should be associated to affected elements.
9901   \return groups of affected elements
9902   \sa DoubleNodeElemGroupsInRegion()
9903  */
9904 //================================================================================
9905
9906 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
9907                                                    const TIDSortedElemSet& theNodesNot,
9908                                                    const TopoDS_Shape&     theShape,
9909                                                    TIDSortedElemSet&       theAffectedElems)
9910 {
9911   if ( theShape.IsNull() )
9912     return false;
9913
9914   const double aTol = Precision::Confusion();
9915   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9916   auto_ptr<_FaceClassifier>              aFaceClassifier;
9917   if ( theShape.ShapeType() == TopAbs_SOLID )
9918   {
9919     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9920     bsc3d->PerformInfinitePoint(aTol);
9921   }
9922   else if (theShape.ShapeType() == TopAbs_FACE )
9923   {
9924     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9925   }
9926
9927   // iterates on indicated elements and get elements by back references from their nodes
9928   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9929   for ( ;  elemItr != theElems.end(); ++elemItr )
9930   {
9931     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9932     if (!anElem)
9933       continue;
9934
9935     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9936     while ( nodeItr->more() )
9937     {
9938       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9939       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9940         continue;
9941       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9942       while ( backElemItr->more() )
9943       {
9944         const SMDS_MeshElement* curElem = backElemItr->next();
9945         if ( curElem && theElems.find(curElem) == theElems.end() &&
9946              ( bsc3d.get() ?
9947                isInside( curElem, *bsc3d, aTol ) :
9948                isInside( curElem, *aFaceClassifier, aTol )))
9949           theAffectedElems.insert( curElem );
9950       }
9951     }
9952   }
9953   return true;
9954 }
9955
9956 //================================================================================
9957 /*!
9958   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9959   \param theElems - group of of elements (edges or faces) to be replicated
9960   \param theNodesNot - group of nodes not to replicate
9961   \param theShape - shape to detect affected elements (element which geometric center
9962   located on or inside shape).
9963   The replicated nodes should be associated to affected elements.
9964   \return TRUE if operation has been completed successfully, FALSE otherwise
9965 */
9966 //================================================================================
9967
9968 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
9969                                             const TIDSortedElemSet& theNodesNot,
9970                                             const TopoDS_Shape&     theShape )
9971 {
9972   if ( theShape.IsNull() )
9973     return false;
9974
9975   const double aTol = Precision::Confusion();
9976   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9977   auto_ptr<_FaceClassifier>              aFaceClassifier;
9978   if ( theShape.ShapeType() == TopAbs_SOLID )
9979   {
9980     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9981     bsc3d->PerformInfinitePoint(aTol);
9982   }
9983   else if (theShape.ShapeType() == TopAbs_FACE )
9984   {
9985     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9986   }
9987
9988   // iterates on indicated elements and get elements by back references from their nodes
9989   TIDSortedElemSet anAffected;
9990   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9991   for ( ;  elemItr != theElems.end(); ++elemItr )
9992   {
9993     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9994     if (!anElem)
9995       continue;
9996
9997     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9998     while ( nodeItr->more() )
9999     {
10000       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10001       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10002         continue;
10003       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10004       while ( backElemItr->more() )
10005       {
10006         const SMDS_MeshElement* curElem = backElemItr->next();
10007         if ( curElem && theElems.find(curElem) == theElems.end() &&
10008              ( bsc3d.get() ?
10009                isInside( curElem, *bsc3d, aTol ) :
10010                isInside( curElem, *aFaceClassifier, aTol )))
10011           anAffected.insert( curElem );
10012       }
10013     }
10014   }
10015   return DoubleNodes( theElems, theNodesNot, anAffected );
10016 }
10017
10018 /*!
10019  *  \brief compute an oriented angle between two planes defined by four points.
10020  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10021  *  @param p0 base of the rotation axe
10022  *  @param p1 extremity of the rotation axe
10023  *  @param g1 belongs to the first plane
10024  *  @param g2 belongs to the second plane
10025  */
10026 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10027 {
10028 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10029 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10030 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10031 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10032   gp_Vec vref(p0, p1);
10033   gp_Vec v1(p0, g1);
10034   gp_Vec v2(p0, g2);
10035   gp_Vec n1 = vref.Crossed(v1);
10036   gp_Vec n2 = vref.Crossed(v2);
10037   return n2.AngleWithRef(n1, vref);
10038 }
10039
10040 /*!
10041  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10042  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10043  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10044  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10045  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10046  * 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.
10047  * 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.
10048  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10049  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10050  * @param theElems - list of groups of volumes, where a group of volume is a set of
10051  * SMDS_MeshElements sorted by Id.
10052  * @param createJointElems - if TRUE, create the elements
10053  * @return TRUE if operation has been completed successfully, FALSE otherwise
10054  */
10055 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10056                                                      bool createJointElems)
10057 {
10058   MESSAGE("----------------------------------------------");
10059   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10060   MESSAGE("----------------------------------------------");
10061
10062   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10063   meshDS->BuildDownWardConnectivity(true);
10064   CHRONO(50);
10065   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10066
10067   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10068   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10069   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10070
10071   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10072   std::map<int,int>celldom; // cell vtkId --> domain
10073   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10074   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10075   faceDomains.clear();
10076   celldom.clear();
10077   cellDomains.clear();
10078   nodeDomains.clear();
10079   std::map<int,int> emptyMap;
10080   std::set<int> emptySet;
10081   emptyMap.clear();
10082
10083   MESSAGE(".. Number of domains :"<<theElems.size());
10084
10085   // Check if the domains do not share an element
10086   for (int idom = 0; idom < theElems.size()-1; idom++)
10087     {
10088 //       MESSAGE("... Check of domain #" << idom);
10089       const TIDSortedElemSet& domain = theElems[idom];
10090       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10091       for (; elemItr != domain.end(); ++elemItr)
10092         {
10093           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10094           int idombisdeb = idom + 1 ;
10095           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10096           {
10097             const TIDSortedElemSet& domainbis = theElems[idombis];
10098             if ( domainbis.count(anElem) )
10099             {
10100               MESSAGE(".... Domain #" << idom);
10101               MESSAGE(".... Domain #" << idombis);
10102               throw SALOME_Exception("The domains are not disjoint.");
10103               return false ;
10104             }
10105           }
10106         }
10107     }
10108
10109   for (int idom = 0; idom < theElems.size(); idom++)
10110     {
10111
10112       // --- build a map (face to duplicate --> volume to modify)
10113       //     with all the faces shared by 2 domains (group of elements)
10114       //     and corresponding volume of this domain, for each shared face.
10115       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10116
10117       MESSAGE("... Neighbors of domain #" << idom);
10118       const TIDSortedElemSet& domain = theElems[idom];
10119       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10120       for (; elemItr != domain.end(); ++elemItr)
10121         {
10122           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10123           if (!anElem)
10124             continue;
10125           int vtkId = anElem->getVtkId();
10126           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10127           int neighborsVtkIds[NBMAXNEIGHBORS];
10128           int downIds[NBMAXNEIGHBORS];
10129           unsigned char downTypes[NBMAXNEIGHBORS];
10130           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10131           for (int n = 0; n < nbNeighbors; n++)
10132             {
10133               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10134               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10135               if (! domain.count(elem)) // neighbor is in another domain : face is shared
10136                 {
10137                   bool ok = false ;
10138                   for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
10139                   {
10140                     // MESSAGE("Domain " << idombis);
10141                     const TIDSortedElemSet& domainbis = theElems[idombis];
10142                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10143                   }
10144                   if ( ok ) // the characteristics of the face is stored
10145                   {
10146                     DownIdType face(downIds[n], downTypes[n]);
10147                     if (!faceDomains.count(face))
10148                       faceDomains[face] = emptyMap; // create an empty entry for face
10149                     if (!faceDomains[face].count(idom))
10150                       {
10151                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10152                         celldom[vtkId] = idom;
10153                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10154                       }
10155                   }
10156                 }
10157             }
10158         }
10159     }
10160
10161   //MESSAGE("Number of shared faces " << faceDomains.size());
10162   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10163
10164   // --- explore the shared faces domain by domain,
10165   //     explore the nodes of the face and see if they belong to a cell in the domain,
10166   //     which has only a node or an edge on the border (not a shared face)
10167
10168   for (int idomain = 0; idomain < theElems.size(); idomain++)
10169     {
10170       //MESSAGE("Domain " << idomain);
10171       const TIDSortedElemSet& domain = theElems[idomain];
10172       itface = faceDomains.begin();
10173       for (; itface != faceDomains.end(); ++itface)
10174         {
10175           std::map<int, int> domvol = itface->second;
10176           if (!domvol.count(idomain))
10177             continue;
10178           DownIdType face = itface->first;
10179           //MESSAGE(" --- face " << face.cellId);
10180           std::set<int> oldNodes;
10181           oldNodes.clear();
10182           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10183           std::set<int>::iterator itn = oldNodes.begin();
10184           for (; itn != oldNodes.end(); ++itn)
10185             {
10186               int oldId = *itn;
10187               //MESSAGE("     node " << oldId);
10188               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10189               for (int i=0; i<l.ncells; i++)
10190                 {
10191                   int vtkId = l.cells[i];
10192                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10193                   if (!domain.count(anElem))
10194                     continue;
10195                   int vtkType = grid->GetCellType(vtkId);
10196                   int downId = grid->CellIdToDownId(vtkId);
10197                   if (downId < 0)
10198                     {
10199                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10200                       continue; // not OK at this stage of the algorithm:
10201                                 //no cells created after BuildDownWardConnectivity
10202                     }
10203                   DownIdType aCell(downId, vtkType);
10204                   if (!cellDomains.count(aCell))
10205                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
10206                   cellDomains[aCell][idomain] = vtkId;
10207                   celldom[vtkId] = idomain;
10208                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10209                 }
10210             }
10211         }
10212     }
10213
10214   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10215   //     for each shared face, get the nodes
10216   //     for each node, for each domain of the face, create a clone of the node
10217
10218   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10219   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10220   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10221
10222   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10223   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10224   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10225
10226   MESSAGE(".. Duplication of the nodes");
10227   for (int idomain = 0; idomain < theElems.size(); idomain++)
10228     {
10229       itface = faceDomains.begin();
10230       for (; itface != faceDomains.end(); ++itface)
10231         {
10232           std::map<int, int> domvol = itface->second;
10233           if (!domvol.count(idomain))
10234             continue;
10235           DownIdType face = itface->first;
10236           //MESSAGE(" --- face " << face.cellId);
10237           std::set<int> oldNodes;
10238           oldNodes.clear();
10239           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10240           std::set<int>::iterator itn = oldNodes.begin();
10241           for (; itn != oldNodes.end(); ++itn)
10242             {
10243               int oldId = *itn;
10244               //MESSAGE("-+-+-a node " << oldId);
10245               if (!nodeDomains.count(oldId))
10246                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10247               if (nodeDomains[oldId].empty())
10248                 {
10249                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10250                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10251                 }
10252               std::map<int, int>::iterator itdom = domvol.begin();
10253               for (; itdom != domvol.end(); ++itdom)
10254                 {
10255                   int idom = itdom->first;
10256                   //MESSAGE("         domain " << idom);
10257                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10258                     {
10259                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10260                         {
10261                           vector<int> orderedDoms;
10262                           //MESSAGE("multiple node " << oldId);
10263                           if (mutipleNodes.count(oldId))
10264                             orderedDoms = mutipleNodes[oldId];
10265                           else
10266                             {
10267                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10268                               for (; it != nodeDomains[oldId].end(); ++it)
10269                                 orderedDoms.push_back(it->first);
10270                             }
10271                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10272                           //stringstream txt;
10273                           //for (int i=0; i<orderedDoms.size(); i++)
10274                           //  txt << orderedDoms[i] << " ";
10275                           //MESSAGE("orderedDoms " << txt.str());
10276                           mutipleNodes[oldId] = orderedDoms;
10277                         }
10278                       double *coords = grid->GetPoint(oldId);
10279                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10280                       int newId = newNode->getVtkId();
10281                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10282                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10283                     }
10284                 }
10285             }
10286         }
10287     }
10288
10289   MESSAGE(".. Creation of elements");
10290   for (int idomain = 0; idomain < theElems.size(); idomain++)
10291     {
10292       itface = faceDomains.begin();
10293       for (; itface != faceDomains.end(); ++itface)
10294         {
10295           std::map<int, int> domvol = itface->second;
10296           if (!domvol.count(idomain))
10297             continue;
10298           DownIdType face = itface->first;
10299           //MESSAGE(" --- face " << face.cellId);
10300           std::set<int> oldNodes;
10301           oldNodes.clear();
10302           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10303           int nbMultipleNodes = 0;
10304           std::set<int>::iterator itn = oldNodes.begin();
10305           for (; itn != oldNodes.end(); ++itn)
10306             {
10307               int oldId = *itn;
10308               if (mutipleNodes.count(oldId))
10309                 nbMultipleNodes++;
10310             }
10311           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10312             {
10313               //MESSAGE("multiple Nodes detected on a shared face");
10314               int downId = itface->first.cellId;
10315               unsigned char cellType = itface->first.cellType;
10316               // --- shared edge or shared face ?
10317               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10318                 {
10319                   int nodes[3];
10320                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10321                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10322                     if (mutipleNodes.count(nodes[i]))
10323                       if (!mutipleNodesToFace.count(nodes[i]))
10324                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10325                 }
10326               else // shared face (between two volumes)
10327                 {
10328                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10329                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10330                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10331                   for (int ie =0; ie < nbEdges; ie++)
10332                     {
10333                       int nodes[3];
10334                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10335                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10336                         {
10337                           vector<int> vn0 = mutipleNodes[nodes[0]];
10338                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10339                           vector<int> doms;
10340                           for (int i0 = 0; i0 < vn0.size(); i0++)
10341                             for (int i1 = 0; i1 < vn1.size(); i1++)
10342                               if (vn0[i0] == vn1[i1])
10343                                 doms.push_back(vn0[i0]);
10344                           if (doms.size() >2)
10345                             {
10346                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10347                               double *coords = grid->GetPoint(nodes[0]);
10348                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10349                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10350                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10351                               gp_Pnt gref;
10352                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10353                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10354                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10355                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10356                               for (int id=0; id < doms.size(); id++)
10357                                 {
10358                                   int idom = doms[id];
10359                                   for (int ivol=0; ivol<nbvol; ivol++)
10360                                     {
10361                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10362                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10363                                       if (theElems[idom].count(elem))
10364                                         {
10365                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10366                                           domvol[idom] = svol;
10367                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10368                                           double values[3];
10369                                           vtkIdType npts = 0;
10370                                           vtkIdType* pts = 0;
10371                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10372                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10373                                           if (id ==0)
10374                                             {
10375                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10376                                               angleDom[idom] = 0;
10377                                             }
10378                                           else
10379                                             {
10380                                               gp_Pnt g(values[0], values[1], values[2]);
10381                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10382                                               //MESSAGE("  angle=" << angleDom[idom]);
10383                                             }
10384                                           break;
10385                                         }
10386                                     }
10387                                 }
10388                               map<double, int> sortedDom; // sort domains by angle
10389                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10390                                 sortedDom[ia->second] = ia->first;
10391                               vector<int> vnodes;
10392                               vector<int> vdom;
10393                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10394                                 {
10395                                   vdom.push_back(ib->second);
10396                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10397                                 }
10398                               for (int ino = 0; ino < nbNodes; ino++)
10399                                 vnodes.push_back(nodes[ino]);
10400                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10401                             }
10402                         }
10403                     }
10404                 }
10405             }
10406         }
10407     }
10408
10409   // --- iterate on shared faces (volumes to modify, face to extrude)
10410   //     get node id's of the face (id SMDS = id VTK)
10411   //     create flat element with old and new nodes if requested
10412
10413   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10414   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10415
10416   std::map<int, std::map<long,int> > nodeQuadDomains;
10417   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10418
10419   MESSAGE(".. Creation of elements: simple junction");
10420   if (createJointElems)
10421     {
10422       int idg;
10423       string joints2DName = "joints2D";
10424       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10425       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10426       string joints3DName = "joints3D";
10427       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10428       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10429
10430       itface = faceDomains.begin();
10431       for (; itface != faceDomains.end(); ++itface)
10432         {
10433           DownIdType face = itface->first;
10434           std::set<int> oldNodes;
10435           std::set<int>::iterator itn;
10436           oldNodes.clear();
10437           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10438
10439           std::map<int, int> domvol = itface->second;
10440           std::map<int, int>::iterator itdom = domvol.begin();
10441           int dom1 = itdom->first;
10442           int vtkVolId = itdom->second;
10443           itdom++;
10444           int dom2 = itdom->first;
10445           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10446                                                              nodeQuadDomains);
10447           stringstream grpname;
10448           grpname << "j_";
10449           if (dom1 < dom2)
10450             grpname << dom1 << "_" << dom2;
10451           else
10452             grpname << dom2 << "_" << dom1;
10453           string namegrp = grpname.str();
10454           if (!mapOfJunctionGroups.count(namegrp))
10455             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10456           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10457           if (sgrp)
10458             sgrp->Add(vol->GetID());
10459           if (vol->GetType() == SMDSAbs_Volume)
10460             joints3DGrp->Add(vol->GetID());
10461           else if (vol->GetType() == SMDSAbs_Face)
10462             joints2DGrp->Add(vol->GetID());
10463         }
10464     }
10465
10466   // --- create volumes on multiple domain intersection if requested
10467   //     iterate on mutipleNodesToFace
10468   //     iterate on edgesMultiDomains
10469
10470   MESSAGE(".. Creation of elements: multiple junction");
10471   if (createJointElems)
10472     {
10473       // --- iterate on mutipleNodesToFace
10474
10475       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
10476       for (; itn != mutipleNodesToFace.end(); ++itn)
10477         {
10478           int node = itn->first;
10479           vector<int> orderDom = itn->second;
10480           vector<vtkIdType> orderedNodes;
10481           for (int idom = 0; idom <orderDom.size(); idom++)
10482             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10483             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10484
10485             stringstream grpname;
10486             grpname << "m2j_";
10487             grpname << 0 << "_" << 0;
10488             int idg;
10489             string namegrp = grpname.str();
10490             if (!mapOfJunctionGroups.count(namegrp))
10491               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10492             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10493             if (sgrp)
10494               sgrp->Add(face->GetID());
10495         }
10496
10497       // --- iterate on edgesMultiDomains
10498
10499       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10500       for (; ite != edgesMultiDomains.end(); ++ite)
10501         {
10502           vector<int> nodes = ite->first;
10503           vector<int> orderDom = ite->second;
10504           vector<vtkIdType> orderedNodes;
10505           if (nodes.size() == 2)
10506             {
10507               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10508               for (int ino=0; ino < nodes.size(); ino++)
10509                 if (orderDom.size() == 3)
10510                   for (int idom = 0; idom <orderDom.size(); idom++)
10511                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10512                 else
10513                   for (int idom = orderDom.size()-1; idom >=0; idom--)
10514                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10515               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10516
10517               int idg;
10518               string namegrp = "jointsMultiples";
10519               if (!mapOfJunctionGroups.count(namegrp))
10520                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10521               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10522               if (sgrp)
10523                 sgrp->Add(vol->GetID());
10524             }
10525           else
10526             {
10527               INFOS("Quadratic multiple joints not implemented");
10528               // TODO quadratic nodes
10529             }
10530         }
10531     }
10532
10533   // --- list the explicit faces and edges of the mesh that need to be modified,
10534   //     i.e. faces and edges built with one or more duplicated nodes.
10535   //     associate these faces or edges to their corresponding domain.
10536   //     only the first domain found is kept when a face or edge is shared
10537
10538   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10539   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10540   faceOrEdgeDom.clear();
10541   feDom.clear();
10542
10543   MESSAGE(".. Modification of elements");
10544   for (int idomain = 0; idomain < theElems.size(); idomain++)
10545     {
10546       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
10547       for (; itnod != nodeDomains.end(); ++itnod)
10548         {
10549           int oldId = itnod->first;
10550           //MESSAGE("     node " << oldId);
10551           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10552           for (int i = 0; i < l.ncells; i++)
10553             {
10554               int vtkId = l.cells[i];
10555               int vtkType = grid->GetCellType(vtkId);
10556               int downId = grid->CellIdToDownId(vtkId);
10557               if (downId < 0)
10558                 continue; // new cells: not to be modified
10559               DownIdType aCell(downId, vtkType);
10560               int volParents[1000];
10561               int nbvol = grid->GetParentVolumes(volParents, vtkId);
10562               for (int j = 0; j < nbvol; j++)
10563                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
10564                   if (!feDom.count(vtkId))
10565                     {
10566                       feDom[vtkId] = idomain;
10567                       faceOrEdgeDom[aCell] = emptyMap;
10568                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
10569                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
10570                       //        << " type " << vtkType << " downId " << downId);
10571                     }
10572             }
10573         }
10574     }
10575
10576   // --- iterate on shared faces (volumes to modify, face to extrude)
10577   //     get node id's of the face
10578   //     replace old nodes by new nodes in volumes, and update inverse connectivity
10579
10580   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
10581   for (int m=0; m<3; m++)
10582     {
10583       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
10584       itface = (*amap).begin();
10585       for (; itface != (*amap).end(); ++itface)
10586         {
10587           DownIdType face = itface->first;
10588           std::set<int> oldNodes;
10589           std::set<int>::iterator itn;
10590           oldNodes.clear();
10591           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10592           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
10593           std::map<int, int> localClonedNodeIds;
10594
10595           std::map<int, int> domvol = itface->second;
10596           std::map<int, int>::iterator itdom = domvol.begin();
10597           for (; itdom != domvol.end(); ++itdom)
10598             {
10599               int idom = itdom->first;
10600               int vtkVolId = itdom->second;
10601               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
10602               localClonedNodeIds.clear();
10603               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
10604                 {
10605                   int oldId = *itn;
10606                   if (nodeDomains[oldId].count(idom))
10607                     {
10608                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
10609                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
10610                     }
10611                 }
10612               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
10613             }
10614         }
10615     }
10616
10617   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
10618   grid->BuildLinks();
10619
10620   CHRONOSTOP(50);
10621   counters::stats();
10622   return true;
10623 }
10624
10625 /*!
10626  * \brief Double nodes on some external faces and create flat elements.
10627  * Flat elements are mainly used by some types of mechanic calculations.
10628  *
10629  * Each group of the list must be constituted of faces.
10630  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10631  * @param theElems - list of groups of faces, where a group of faces is a set of
10632  * SMDS_MeshElements sorted by Id.
10633  * @return TRUE if operation has been completed successfully, FALSE otherwise
10634  */
10635 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
10636 {
10637   MESSAGE("-------------------------------------------------");
10638   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
10639   MESSAGE("-------------------------------------------------");
10640
10641   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10642
10643   // --- For each group of faces
10644   //     duplicate the nodes, create a flat element based on the face
10645   //     replace the nodes of the faces by their clones
10646
10647   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
10648   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
10649   clonedNodes.clear();
10650   intermediateNodes.clear();
10651   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10652   mapOfJunctionGroups.clear();
10653
10654   for (int idom = 0; idom < theElems.size(); idom++)
10655     {
10656       const TIDSortedElemSet& domain = theElems[idom];
10657       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10658       for (; elemItr != domain.end(); ++elemItr)
10659         {
10660           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10661           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
10662           if (!aFace)
10663             continue;
10664           // MESSAGE("aFace=" << aFace->GetID());
10665           bool isQuad = aFace->IsQuadratic();
10666           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
10667
10668           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
10669
10670           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
10671           while (nodeIt->more())
10672             {
10673               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
10674               bool isMedium = isQuad && (aFace->IsMediumNode(node));
10675               if (isMedium)
10676                 ln2.push_back(node);
10677               else
10678                 ln0.push_back(node);
10679
10680               const SMDS_MeshNode* clone = 0;
10681               if (!clonedNodes.count(node))
10682                 {
10683                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
10684                   clonedNodes[node] = clone;
10685                 }
10686               else
10687                 clone = clonedNodes[node];
10688
10689               if (isMedium)
10690                 ln3.push_back(clone);
10691               else
10692                 ln1.push_back(clone);
10693
10694               const SMDS_MeshNode* inter = 0;
10695               if (isQuad && (!isMedium))
10696                 {
10697                   if (!intermediateNodes.count(node))
10698                     {
10699                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
10700                       intermediateNodes[node] = inter;
10701                     }
10702                   else
10703                     inter = intermediateNodes[node];
10704                   ln4.push_back(inter);
10705                 }
10706             }
10707
10708           // --- extrude the face
10709
10710           vector<const SMDS_MeshNode*> ln;
10711           SMDS_MeshVolume* vol = 0;
10712           vtkIdType aType = aFace->GetVtkType();
10713           switch (aType)
10714           {
10715             case VTK_TRIANGLE:
10716               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
10717               // MESSAGE("vol prism " << vol->GetID());
10718               ln.push_back(ln1[0]);
10719               ln.push_back(ln1[1]);
10720               ln.push_back(ln1[2]);
10721               break;
10722             case VTK_QUAD:
10723               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
10724               // MESSAGE("vol hexa " << vol->GetID());
10725               ln.push_back(ln1[0]);
10726               ln.push_back(ln1[1]);
10727               ln.push_back(ln1[2]);
10728               ln.push_back(ln1[3]);
10729               break;
10730             case VTK_QUADRATIC_TRIANGLE:
10731               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
10732                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
10733               // MESSAGE("vol quad prism " << vol->GetID());
10734               ln.push_back(ln1[0]);
10735               ln.push_back(ln1[1]);
10736               ln.push_back(ln1[2]);
10737               ln.push_back(ln3[0]);
10738               ln.push_back(ln3[1]);
10739               ln.push_back(ln3[2]);
10740               break;
10741             case VTK_QUADRATIC_QUAD:
10742 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
10743 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
10744 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
10745               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
10746                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
10747                                       ln4[0], ln4[1], ln4[2], ln4[3]);
10748               // MESSAGE("vol quad hexa " << vol->GetID());
10749               ln.push_back(ln1[0]);
10750               ln.push_back(ln1[1]);
10751               ln.push_back(ln1[2]);
10752               ln.push_back(ln1[3]);
10753               ln.push_back(ln3[0]);
10754               ln.push_back(ln3[1]);
10755               ln.push_back(ln3[2]);
10756               ln.push_back(ln3[3]);
10757               break;
10758             case VTK_POLYGON:
10759               break;
10760             default:
10761               break;
10762           }
10763
10764           if (vol)
10765             {
10766               stringstream grpname;
10767               grpname << "jf_";
10768               grpname << idom;
10769               int idg;
10770               string namegrp = grpname.str();
10771               if (!mapOfJunctionGroups.count(namegrp))
10772                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10773               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10774               if (sgrp)
10775                 sgrp->Add(vol->GetID());
10776             }
10777
10778           // --- modify the face
10779
10780           aFace->ChangeNodes(&ln[0], ln.size());
10781         }
10782     }
10783   return true;
10784 }
10785
10786 /*!
10787  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
10788  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
10789  *  groups of faces to remove inside the object, (idem edges).
10790  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
10791  */
10792 void SMESH_MeshEditor::CreateHoleSkin(double radius,
10793                                       const TopoDS_Shape& theShape,
10794                                       SMESH_NodeSearcher* theNodeSearcher,
10795                                       const char* groupName,
10796                                       std::vector<double>&   nodesCoords,
10797                                       std::vector<std::vector<int> >& listOfListOfNodes)
10798 {
10799   MESSAGE("--------------------------------");
10800   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
10801   MESSAGE("--------------------------------");
10802
10803   // --- zone of volumes to remove is given :
10804   //     1 either by a geom shape (one or more vertices) and a radius,
10805   //     2 either by a group of nodes (representative of the shape)to use with the radius,
10806   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
10807   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
10808   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
10809   //     defined by it's name.
10810
10811   SMESHDS_GroupBase* groupDS = 0;
10812   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
10813   while ( groupIt->more() )
10814     {
10815       groupDS = 0;
10816       SMESH_Group * group = groupIt->next();
10817       if ( !group ) continue;
10818       groupDS = group->GetGroupDS();
10819       if ( !groupDS || groupDS->IsEmpty() ) continue;
10820       std::string grpName = group->GetName();
10821       //MESSAGE("grpName=" << grpName);
10822       if (grpName == groupName)
10823         break;
10824       else
10825         groupDS = 0;
10826     }
10827
10828   bool isNodeGroup = false;
10829   bool isNodeCoords = false;
10830   if (groupDS)
10831     {
10832       if (groupDS->GetType() != SMDSAbs_Node)
10833         return;
10834       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
10835     }
10836
10837   if (nodesCoords.size() > 0)
10838     isNodeCoords = true; // a list o nodes given by their coordinates
10839   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
10840
10841   // --- define groups to build
10842
10843   int idg; // --- group of SMDS volumes
10844   string grpvName = groupName;
10845   grpvName += "_vol";
10846   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
10847   if (!grp)
10848     {
10849       MESSAGE("group not created " << grpvName);
10850       return;
10851     }
10852   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
10853
10854   int idgs; // --- group of SMDS faces on the skin
10855   string grpsName = groupName;
10856   grpsName += "_skin";
10857   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
10858   if (!grps)
10859     {
10860       MESSAGE("group not created " << grpsName);
10861       return;
10862     }
10863   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
10864
10865   int idgi; // --- group of SMDS faces internal (several shapes)
10866   string grpiName = groupName;
10867   grpiName += "_internalFaces";
10868   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
10869   if (!grpi)
10870     {
10871       MESSAGE("group not created " << grpiName);
10872       return;
10873     }
10874   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
10875
10876   int idgei; // --- group of SMDS faces internal (several shapes)
10877   string grpeiName = groupName;
10878   grpeiName += "_internalEdges";
10879   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
10880   if (!grpei)
10881     {
10882       MESSAGE("group not created " << grpeiName);
10883       return;
10884     }
10885   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
10886
10887   // --- build downward connectivity
10888
10889   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10890   meshDS->BuildDownWardConnectivity(true);
10891   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
10892
10893   // --- set of volumes detected inside
10894
10895   std::set<int> setOfInsideVol;
10896   std::set<int> setOfVolToCheck;
10897
10898   std::vector<gp_Pnt> gpnts;
10899   gpnts.clear();
10900
10901   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
10902     {
10903       MESSAGE("group of nodes provided");
10904       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
10905       while ( elemIt->more() )
10906         {
10907           const SMDS_MeshElement* elem = elemIt->next();
10908           if (!elem)
10909             continue;
10910           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
10911           if (!node)
10912             continue;
10913           SMDS_MeshElement* vol = 0;
10914           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
10915           while (volItr->more())
10916             {
10917               vol = (SMDS_MeshElement*)volItr->next();
10918               setOfInsideVol.insert(vol->getVtkId());
10919               sgrp->Add(vol->GetID());
10920             }
10921         }
10922     }
10923   else if (isNodeCoords)
10924     {
10925       MESSAGE("list of nodes coordinates provided");
10926       int i = 0;
10927       int k = 0;
10928       while (i < nodesCoords.size()-2)
10929         {
10930           double x = nodesCoords[i++];
10931           double y = nodesCoords[i++];
10932           double z = nodesCoords[i++];
10933           gp_Pnt p = gp_Pnt(x, y ,z);
10934           gpnts.push_back(p);
10935           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
10936           k++;
10937         }
10938     }
10939   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
10940     {
10941       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
10942       TopTools_IndexedMapOfShape vertexMap;
10943       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
10944       gp_Pnt p = gp_Pnt(0,0,0);
10945       if (vertexMap.Extent() < 1)
10946         return;
10947
10948       for ( int i = 1; i <= vertexMap.Extent(); ++i )
10949         {
10950           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
10951           p = BRep_Tool::Pnt(vertex);
10952           gpnts.push_back(p);
10953           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
10954         }
10955     }
10956
10957   if (gpnts.size() > 0)
10958     {
10959       int nodeId = 0;
10960       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
10961       if (startNode)
10962         nodeId = startNode->GetID();
10963       MESSAGE("nodeId " << nodeId);
10964
10965       double radius2 = radius*radius;
10966       MESSAGE("radius2 " << radius2);
10967
10968       // --- volumes on start node
10969
10970       setOfVolToCheck.clear();
10971       SMDS_MeshElement* startVol = 0;
10972       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
10973       while (volItr->more())
10974         {
10975           startVol = (SMDS_MeshElement*)volItr->next();
10976           setOfVolToCheck.insert(startVol->getVtkId());
10977         }
10978       if (setOfVolToCheck.empty())
10979         {
10980           MESSAGE("No volumes found");
10981           return;
10982         }
10983
10984       // --- starting with central volumes then their neighbors, check if they are inside
10985       //     or outside the domain, until no more new neighbor volume is inside.
10986       //     Fill the group of inside volumes
10987
10988       std::map<int, double> mapOfNodeDistance2;
10989       mapOfNodeDistance2.clear();
10990       std::set<int> setOfOutsideVol;
10991       while (!setOfVolToCheck.empty())
10992         {
10993           std::set<int>::iterator it = setOfVolToCheck.begin();
10994           int vtkId = *it;
10995           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10996           bool volInside = false;
10997           vtkIdType npts = 0;
10998           vtkIdType* pts = 0;
10999           grid->GetCellPoints(vtkId, npts, pts);
11000           for (int i=0; i<npts; i++)
11001             {
11002               double distance2 = 0;
11003               if (mapOfNodeDistance2.count(pts[i]))
11004                 {
11005                   distance2 = mapOfNodeDistance2[pts[i]];
11006                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11007                 }
11008               else
11009                 {
11010                   double *coords = grid->GetPoint(pts[i]);
11011                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11012                   distance2 = 1.E40;
11013                   for (int j=0; j<gpnts.size(); j++)
11014                     {
11015                       double d2 = aPoint.SquareDistance(gpnts[j]);
11016                       if (d2 < distance2)
11017                         {
11018                           distance2 = d2;
11019                           if (distance2 < radius2)
11020                             break;
11021                         }
11022                     }
11023                   mapOfNodeDistance2[pts[i]] = distance2;
11024                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11025                 }
11026               if (distance2 < radius2)
11027                 {
11028                   volInside = true; // one or more nodes inside the domain
11029                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11030                   break;
11031                 }
11032             }
11033           if (volInside)
11034             {
11035               setOfInsideVol.insert(vtkId);
11036               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11037               int neighborsVtkIds[NBMAXNEIGHBORS];
11038               int downIds[NBMAXNEIGHBORS];
11039               unsigned char downTypes[NBMAXNEIGHBORS];
11040               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11041               for (int n = 0; n < nbNeighbors; n++)
11042                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11043                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11044             }
11045           else
11046             {
11047               setOfOutsideVol.insert(vtkId);
11048               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11049             }
11050           setOfVolToCheck.erase(vtkId);
11051         }
11052     }
11053
11054   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11055   //     If yes, add the volume to the inside set
11056
11057   bool addedInside = true;
11058   std::set<int> setOfVolToReCheck;
11059   while (addedInside)
11060     {
11061       MESSAGE(" --------------------------- re check");
11062       addedInside = false;
11063       std::set<int>::iterator itv = setOfInsideVol.begin();
11064       for (; itv != setOfInsideVol.end(); ++itv)
11065         {
11066           int vtkId = *itv;
11067           int neighborsVtkIds[NBMAXNEIGHBORS];
11068           int downIds[NBMAXNEIGHBORS];
11069           unsigned char downTypes[NBMAXNEIGHBORS];
11070           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11071           for (int n = 0; n < nbNeighbors; n++)
11072             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11073               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11074         }
11075       setOfVolToCheck = setOfVolToReCheck;
11076       setOfVolToReCheck.clear();
11077       while  (!setOfVolToCheck.empty())
11078         {
11079           std::set<int>::iterator it = setOfVolToCheck.begin();
11080           int vtkId = *it;
11081           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11082             {
11083               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11084               int countInside = 0;
11085               int neighborsVtkIds[NBMAXNEIGHBORS];
11086               int downIds[NBMAXNEIGHBORS];
11087               unsigned char downTypes[NBMAXNEIGHBORS];
11088               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11089               for (int n = 0; n < nbNeighbors; n++)
11090                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11091                   countInside++;
11092               MESSAGE("countInside " << countInside);
11093               if (countInside > 1)
11094                 {
11095                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11096                   setOfInsideVol.insert(vtkId);
11097                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11098                   addedInside = true;
11099                 }
11100               else
11101                 setOfVolToReCheck.insert(vtkId);
11102             }
11103           setOfVolToCheck.erase(vtkId);
11104         }
11105     }
11106
11107   // --- map of Downward faces at the boundary, inside the global volume
11108   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11109   //     fill group of SMDS faces inside the volume (when several volume shapes)
11110   //     fill group of SMDS faces on the skin of the global volume (if skin)
11111
11112   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11113   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11114   std::set<int>::iterator it = setOfInsideVol.begin();
11115   for (; it != setOfInsideVol.end(); ++it)
11116     {
11117       int vtkId = *it;
11118       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11119       int neighborsVtkIds[NBMAXNEIGHBORS];
11120       int downIds[NBMAXNEIGHBORS];
11121       unsigned char downTypes[NBMAXNEIGHBORS];
11122       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11123       for (int n = 0; n < nbNeighbors; n++)
11124         {
11125           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11126           if (neighborDim == 3)
11127             {
11128               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11129                 {
11130                   DownIdType face(downIds[n], downTypes[n]);
11131                   boundaryFaces[face] = vtkId;
11132                 }
11133               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11134               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11135               if (vtkFaceId >= 0)
11136                 {
11137                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11138                   // find also the smds edges on this face
11139                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11140                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11141                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11142                   for (int i = 0; i < nbEdges; i++)
11143                     {
11144                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11145                       if (vtkEdgeId >= 0)
11146                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11147                     }
11148                 }
11149             }
11150           else if (neighborDim == 2) // skin of the volume
11151             {
11152               DownIdType face(downIds[n], downTypes[n]);
11153               skinFaces[face] = vtkId;
11154               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11155               if (vtkFaceId >= 0)
11156                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11157             }
11158         }
11159     }
11160
11161   // --- identify the edges constituting the wire of each subshape on the skin
11162   //     define polylines with the nodes of edges, equivalent to wires
11163   //     project polylines on subshapes, and partition, to get geom faces
11164
11165   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11166   std::set<int> emptySet;
11167   emptySet.clear();
11168   std::set<int> shapeIds;
11169
11170   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11171   while (itelem->more())
11172     {
11173       const SMDS_MeshElement *elem = itelem->next();
11174       int shapeId = elem->getshapeId();
11175       int vtkId = elem->getVtkId();
11176       if (!shapeIdToVtkIdSet.count(shapeId))
11177         {
11178           shapeIdToVtkIdSet[shapeId] = emptySet;
11179           shapeIds.insert(shapeId);
11180         }
11181       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11182     }
11183
11184   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11185   std::set<DownIdType, DownIdCompare> emptyEdges;
11186   emptyEdges.clear();
11187
11188   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11189   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11190     {
11191       int shapeId = itShape->first;
11192       MESSAGE(" --- Shape ID --- "<< shapeId);
11193       shapeIdToEdges[shapeId] = emptyEdges;
11194
11195       std::vector<int> nodesEdges;
11196
11197       std::set<int>::iterator its = itShape->second.begin();
11198       for (; its != itShape->second.end(); ++its)
11199         {
11200           int vtkId = *its;
11201           MESSAGE("     " << vtkId);
11202           int neighborsVtkIds[NBMAXNEIGHBORS];
11203           int downIds[NBMAXNEIGHBORS];
11204           unsigned char downTypes[NBMAXNEIGHBORS];
11205           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11206           for (int n = 0; n < nbNeighbors; n++)
11207             {
11208               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11209                 continue;
11210               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11211               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11212               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11213                 {
11214                   DownIdType edge(downIds[n], downTypes[n]);
11215                   if (!shapeIdToEdges[shapeId].count(edge))
11216                     {
11217                       shapeIdToEdges[shapeId].insert(edge);
11218                       int vtkNodeId[3];
11219                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11220                       nodesEdges.push_back(vtkNodeId[0]);
11221                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11222                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11223                     }
11224                 }
11225             }
11226         }
11227
11228       std::list<int> order;
11229       order.clear();
11230       if (nodesEdges.size() > 0)
11231         {
11232           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11233           nodesEdges[0] = -1;
11234           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11235           nodesEdges[1] = -1; // do not reuse this edge
11236           bool found = true;
11237           while (found)
11238             {
11239               int nodeTofind = order.back(); // try first to push back
11240               int i = 0;
11241               for (i = 0; i<nodesEdges.size(); i++)
11242                 if (nodesEdges[i] == nodeTofind)
11243                   break;
11244               if (i == nodesEdges.size())
11245                 found = false; // no follower found on back
11246               else
11247                 {
11248                   if (i%2) // odd ==> use the previous one
11249                     if (nodesEdges[i-1] < 0)
11250                       found = false;
11251                     else
11252                       {
11253                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11254                         nodesEdges[i-1] = -1;
11255                       }
11256                   else // even ==> use the next one
11257                     if (nodesEdges[i+1] < 0)
11258                       found = false;
11259                     else
11260                       {
11261                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11262                         nodesEdges[i+1] = -1;
11263                       }
11264                 }
11265               if (found)
11266                 continue;
11267               // try to push front
11268               found = true;
11269               nodeTofind = order.front(); // try to push front
11270               for (i = 0; i<nodesEdges.size(); i++)
11271                 if (nodesEdges[i] == nodeTofind)
11272                   break;
11273               if (i == nodesEdges.size())
11274                 {
11275                   found = false; // no predecessor found on front
11276                   continue;
11277                 }
11278               if (i%2) // odd ==> use the previous one
11279                 if (nodesEdges[i-1] < 0)
11280                   found = false;
11281                 else
11282                   {
11283                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11284                     nodesEdges[i-1] = -1;
11285                   }
11286               else // even ==> use the next one
11287                 if (nodesEdges[i+1] < 0)
11288                   found = false;
11289                 else
11290                   {
11291                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11292                     nodesEdges[i+1] = -1;
11293                   }
11294             }
11295         }
11296
11297
11298       std::vector<int> nodes;
11299       nodes.push_back(shapeId);
11300       std::list<int>::iterator itl = order.begin();
11301       for (; itl != order.end(); itl++)
11302         {
11303           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11304           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11305         }
11306       listOfListOfNodes.push_back(nodes);
11307     }
11308
11309   //     partition geom faces with blocFissure
11310   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11311   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11312
11313   return;
11314 }
11315
11316
11317 //================================================================================
11318 /*!
11319  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11320  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11321  * \return TRUE if operation has been completed successfully, FALSE otherwise
11322  */
11323 //================================================================================
11324
11325 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11326 {
11327   // iterates on volume elements and detect all free faces on them
11328   SMESHDS_Mesh* aMesh = GetMeshDS();
11329   if (!aMesh)
11330     return false;
11331   //bool res = false;
11332   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11333   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11334   while(vIt->more())
11335   {
11336     const SMDS_MeshVolume* volume = vIt->next();
11337     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11338     vTool.SetExternalNormal();
11339     //const bool isPoly = volume->IsPoly();
11340     const int iQuad = volume->IsQuadratic();
11341     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11342     {
11343       if (!vTool.IsFreeFace(iface))
11344         continue;
11345       nbFree++;
11346       vector<const SMDS_MeshNode *> nodes;
11347       int nbFaceNodes = vTool.NbFaceNodes(iface);
11348       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11349       int inode = 0;
11350       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11351         nodes.push_back(faceNodes[inode]);
11352       if (iQuad) { // add medium nodes
11353         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11354           nodes.push_back(faceNodes[inode]);
11355         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11356           nodes.push_back(faceNodes[8]);
11357       }
11358       // add new face based on volume nodes
11359       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11360         nbExisted++;
11361         continue; // face already exsist
11362       }
11363       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11364       nbCreated++;
11365     }
11366   }
11367   return ( nbFree==(nbExisted+nbCreated) );
11368 }
11369
11370 namespace
11371 {
11372   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11373   {
11374     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11375       return n;
11376     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11377   }
11378 }
11379 //================================================================================
11380 /*!
11381  * \brief Creates missing boundary elements
11382  *  \param elements - elements whose boundary is to be checked
11383  *  \param dimension - defines type of boundary elements to create
11384  *  \param group - a group to store created boundary elements in
11385  *  \param targetMesh - a mesh to store created boundary elements in
11386  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11387  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11388  *                                boundary elements will be copied into the targetMesh
11389  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11390  *                                boundary elements will be added into the new group
11391  *  \param aroundElements - if true, elements will be created on boundary of given
11392  *                          elements else, on boundary of the whole mesh.
11393  * \return nb of added boundary elements
11394  */
11395 //================================================================================
11396
11397 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11398                                        Bnd_Dimension           dimension,
11399                                        SMESH_Group*            group/*=0*/,
11400                                        SMESH_Mesh*             targetMesh/*=0*/,
11401                                        bool                    toCopyElements/*=false*/,
11402                                        bool                    toCopyExistingBoundary/*=false*/,
11403                                        bool                    toAddExistingBondary/*= false*/,
11404                                        bool                    aroundElements/*= false*/)
11405 {
11406   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11407   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11408   // hope that all elements are of the same type, do not check them all
11409   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11410     throw SALOME_Exception(LOCALIZED("wrong element type"));
11411
11412   if ( !targetMesh )
11413     toCopyElements = toCopyExistingBoundary = false;
11414
11415   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11416   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11417   int nbAddedBnd = 0;
11418
11419   // editor adding present bnd elements and optionally holding elements to add to the group
11420   SMESH_MeshEditor* presentEditor;
11421   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11422   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11423
11424   SMESH_MesherHelper helper( *myMesh );
11425   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11426   SMDS_VolumeTool vTool;
11427   TIDSortedElemSet avoidSet;
11428   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11429   int inode;
11430
11431   typedef vector<const SMDS_MeshNode*> TConnectivity;
11432
11433   SMDS_ElemIteratorPtr eIt;
11434   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11435   else                  eIt = elemSetIterator( elements );
11436
11437   while (eIt->more())
11438   {
11439     const SMDS_MeshElement* elem = eIt->next();
11440     const int              iQuad = elem->IsQuadratic();
11441
11442     // ------------------------------------------------------------------------------------
11443     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11444     // ------------------------------------------------------------------------------------
11445     vector<const SMDS_MeshElement*> presentBndElems;
11446     vector<TConnectivity>           missingBndElems;
11447     TConnectivity nodes, elemNodes;
11448     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11449     {
11450       vTool.SetExternalNormal();
11451       const SMDS_MeshElement* otherVol = 0;
11452       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11453       {
11454         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11455              ( !aroundElements || elements.count( otherVol )))
11456           continue;
11457         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11458         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
11459         if ( missType == SMDSAbs_Edge ) // boundary edges
11460         {
11461           nodes.resize( 2+iQuad );
11462           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11463           {
11464             for ( int j = 0; j < nodes.size(); ++j )
11465               nodes[j] =nn[i+j];
11466             if ( const SMDS_MeshElement* edge =
11467                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11468               presentBndElems.push_back( edge );
11469             else
11470               missingBndElems.push_back( nodes );
11471           }
11472         }
11473         else // boundary face
11474         {
11475           nodes.clear();
11476           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11477             nodes.push_back( nn[inode] ); // add corner nodes
11478           if (iQuad)
11479             for ( inode = 1; inode < nbFaceNodes; inode += 2)
11480               nodes.push_back( nn[inode] ); // add medium nodes
11481           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11482           if ( iCenter > 0 )
11483             nodes.push_back( vTool.GetNodes()[ iCenter ] );
11484
11485           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11486                                                                SMDSAbs_Face, /*noMedium=*/false ))
11487             presentBndElems.push_back( f );
11488           else
11489             missingBndElems.push_back( nodes );
11490
11491           if ( targetMesh != myMesh )
11492           {
11493             // add 1D elements on face boundary to be added to a new mesh
11494             const SMDS_MeshElement* edge;
11495             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11496             {
11497               if ( iQuad )
11498                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11499               else
11500                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11501               if ( edge && avoidSet.insert( edge ).second )
11502                 presentBndElems.push_back( edge );
11503             }
11504           }
11505         }
11506       }
11507     }
11508     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
11509     {
11510       avoidSet.clear(), avoidSet.insert( elem );
11511       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
11512                         SMDS_MeshElement::iterator() );
11513       elemNodes.push_back( elemNodes[0] );
11514       nodes.resize( 2 + iQuad );
11515       const int nbLinks = elem->NbCornerNodes();
11516       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
11517       {
11518         nodes[0] = elemNodes[iN];
11519         nodes[1] = elemNodes[iN+1+iQuad];
11520         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11521           continue; // not free link
11522
11523         if ( iQuad ) nodes[2] = elemNodes[iN+1];
11524         if ( const SMDS_MeshElement* edge =
11525              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11526           presentBndElems.push_back( edge );
11527         else
11528           missingBndElems.push_back( nodes );
11529       }
11530     }
11531
11532     // ---------------------------------
11533     // 2. Add missing boundary elements
11534     // ---------------------------------
11535     if ( targetMesh != myMesh )
11536       // instead of making a map of nodes in this mesh and targetMesh,
11537       // we create nodes with same IDs.
11538       for ( int i = 0; i < missingBndElems.size(); ++i )
11539       {
11540         TConnectivity& srcNodes = missingBndElems[i];
11541         TConnectivity  nodes( srcNodes.size() );
11542         for ( inode = 0; inode < nodes.size(); ++inode )
11543           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11544         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11545                                                                    missType,
11546                                                                    /*noMedium=*/false))
11547           continue;
11548         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11549         ++nbAddedBnd;
11550       }
11551     else
11552       for ( int i = 0; i < missingBndElems.size(); ++i )
11553       {
11554         TConnectivity& nodes = missingBndElems[i];
11555         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11556                                                                    missType,
11557                                                                    /*noMedium=*/false))
11558           continue;
11559         SMDS_MeshElement* elem =
11560           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11561         ++nbAddedBnd;
11562
11563         // try to set a new element to a shape
11564         if ( myMesh->HasShapeToMesh() )
11565         {
11566           bool ok = true;
11567           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11568           const int nbN = nodes.size() / (iQuad+1 );
11569           for ( inode = 0; inode < nbN && ok; ++inode )
11570           {
11571             pair<int, TopAbs_ShapeEnum> i_stype =
11572               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11573             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11574               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11575           }
11576           if ( ok && mediumShapes.size() > 1 )
11577           {
11578             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11579             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11580             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11581             {
11582               if (( ok = ( stype_i->first != stype_i_0.first )))
11583                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11584                                         aMesh->IndexToShape( stype_i_0.second ));
11585             }
11586           }
11587           if ( ok && mediumShapes.begin()->first == missShapeType )
11588             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11589         }
11590       }
11591
11592     // ----------------------------------
11593     // 3. Copy present boundary elements
11594     // ----------------------------------
11595     if ( toCopyExistingBoundary )
11596       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11597       {
11598         const SMDS_MeshElement* e = presentBndElems[i];
11599         TConnectivity nodes( e->NbNodes() );
11600         for ( inode = 0; inode < nodes.size(); ++inode )
11601           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11602         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11603       }
11604     else // store present elements to add them to a group
11605       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11606       {
11607         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11608       }
11609
11610   } // loop on given elements
11611
11612   // ---------------------------------------------
11613   // 4. Fill group with boundary elements
11614   // ---------------------------------------------
11615   if ( group )
11616   {
11617     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11618       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11619         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11620   }
11621   tgtEditor.myLastCreatedElems.Clear();
11622   tgtEditor2.myLastCreatedElems.Clear();
11623
11624   // -----------------------
11625   // 5. Copy given elements
11626   // -----------------------
11627   if ( toCopyElements && targetMesh != myMesh )
11628   {
11629     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11630     else                  eIt = elemSetIterator( elements );
11631     while (eIt->more())
11632     {
11633       const SMDS_MeshElement* elem = eIt->next();
11634       TConnectivity nodes( elem->NbNodes() );
11635       for ( inode = 0; inode < nodes.size(); ++inode )
11636         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11637       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11638
11639       tgtEditor.myLastCreatedElems.Clear();
11640     }
11641   }
11642   return nbAddedBnd;
11643 }