Salome HOME
22599: EDF 8159 SMESH: Bad groups created by extrusion
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], ID);
226         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9] );
229       }
230       else if (nbnode == 12) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10], node[11], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12] );
247       }
248       else if (nbnode == 15) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523     for ( ; id_sm != id2sm.end(); ++id_sm )
524       if ( id_sm->second->Contains( theElem ))
525         return id_sm->first;
526   }
527
528   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
529   return 0;
530 }
531
532 //=======================================================================
533 //function : IsMedium
534 //purpose  :
535 //=======================================================================
536
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
538                                 const SMDSAbs_ElementType typeToCheck)
539 {
540   bool isMedium = false;
541   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542   while (it->more() && !isMedium ) {
543     const SMDS_MeshElement* elem = it->next();
544     isMedium = elem->IsMediumNode(node);
545   }
546   return isMedium;
547 }
548
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose  : Shift nodes in the array corresponded to quadratic triangle
552 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
554
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 {
557   const SMDS_MeshNode* nd1 = aNodes[0];
558   aNodes[0] = aNodes[1];
559   aNodes[1] = aNodes[2];
560   aNodes[2] = nd1;
561   const SMDS_MeshNode* nd2 = aNodes[3];
562   aNodes[3] = aNodes[4];
563   aNodes[4] = aNodes[5];
564   aNodes[5] = nd2;
565 }
566
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose  : return number of the edges connected with the theNode.
570 //           if theEdges has connections with the other type of the
571 //           elements, return -1
572 //=======================================================================
573
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 {
576   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577   // int nb=0;
578   // while(elemIt->more()) {
579   //   elemIt->next();
580   //   nb++;
581   // }
582   // return nb;
583   return theNode->NbInverseElements();
584 }
585
586 //=======================================================================
587 //function : getNodesFromTwoTria
588 //purpose  : 
589 //=======================================================================
590
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592                                 const SMDS_MeshElement * theTria2,
593                                 vector< const SMDS_MeshNode*>& N1,
594                                 vector< const SMDS_MeshNode*>& N2)
595 {
596   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597   if ( N1.size() < 6 ) return false;
598   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599   if ( N2.size() < 6 ) return false;
600
601   int sames[3] = {-1,-1,-1};
602   int nbsames = 0;
603   int i, j;
604   for(i=0; i<3; i++) {
605     for(j=0; j<3; j++) {
606       if(N1[i]==N2[j]) {
607         sames[i] = j;
608         nbsames++;
609         break;
610       }
611     }
612   }
613   if(nbsames!=2) return false;
614   if(sames[0]>-1) {
615     shiftNodesQuadTria(N1);
616     if(sames[1]>-1) {
617       shiftNodesQuadTria(N1);
618     }
619   }
620   i = sames[0] + sames[1] + sames[2];
621   for(; i<2; i++) {
622     shiftNodesQuadTria(N2);
623   }
624   // now we receive following N1 and N2 (using numeration as in the image below)
625   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
626   // i.e. first nodes from both arrays form a new diagonal
627   return true;
628 }
629
630 //=======================================================================
631 //function : InverseDiag
632 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
633 //           but having other common link.
634 //           Return False if args are improper
635 //=======================================================================
636
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638                                     const SMDS_MeshElement * theTria2 )
639 {
640   MESSAGE("InverseDiag");
641   myLastCreatedElems.Clear();
642   myLastCreatedNodes.Clear();
643
644   if (!theTria1 || !theTria2)
645     return false;
646
647   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648   if (!F1) return false;
649   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650   if (!F2) return false;
651   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653
654     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
656     //    |/ |                                         | \|
657     //  B +--+ 2                                     B +--+ 2
658
659     // put nodes in array and find out indices of the same ones
660     const SMDS_MeshNode* aNodes [6];
661     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662     int i = 0;
663     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664     while ( it->more() ) {
665       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666
667       if ( i > 2 ) // theTria2
668         // find same node of theTria1
669         for ( int j = 0; j < 3; j++ )
670           if ( aNodes[ i ] == aNodes[ j ]) {
671             sameInd[ j ] = i;
672             sameInd[ i ] = j;
673             break;
674           }
675       // next
676       i++;
677       if ( i == 3 ) {
678         if ( it->more() )
679           return false; // theTria1 is not a triangle
680         it = theTria2->nodesIterator();
681       }
682       if ( i == 6 && it->more() )
683         return false; // theTria2 is not a triangle
684     }
685
686     // find indices of 1,2 and of A,B in theTria1
687     int iA = -1, iB = 0, i1 = 0, i2 = 0;
688     for ( i = 0; i < 6; i++ ) {
689       if ( sameInd [ i ] == -1 ) {
690         if ( i < 3 ) i1 = i;
691         else         i2 = i;
692       }
693       else if (i < 3) {
694         if ( iA >= 0) iB = i;
695         else          iA = i;
696       }
697     }
698     // nodes 1 and 2 should not be the same
699     if ( aNodes[ i1 ] == aNodes[ i2 ] )
700       return false;
701
702     // theTria1: A->2
703     aNodes[ iA ] = aNodes[ i2 ];
704     // theTria2: B->1
705     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706
707     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
709
710     return true;
711
712   } // end if(F1 && F2)
713
714   // check case of quadratic faces
715   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717     return false;
718   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
720     return false;
721
722   //       5
723   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
725   //    |   / |
726   //  7 +  +  + 6
727   //    | /9  |
728   //    |/    |
729   //  4 +--+--+ 3
730   //       8
731
732   vector< const SMDS_MeshNode* > N1;
733   vector< const SMDS_MeshNode* > N2;
734   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735     return false;
736   // now we receive following N1 and N2 (using numeration as above image)
737   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
738   // i.e. first nodes from both arrays determ new diagonal
739
740   vector< const SMDS_MeshNode*> N1new( N1.size() );
741   vector< const SMDS_MeshNode*> N2new( N2.size() );
742   N1new.back() = N1.back(); // central node of biquadratic
743   N2new.back() = N2.back();
744   N1new[0] = N1[0];  N2new[0] = N1[0];
745   N1new[1] = N2[0];  N2new[1] = N1[1];
746   N1new[2] = N2[1];  N2new[2] = N2[0];
747   N1new[3] = N1[4];  N2new[3] = N1[3];
748   N1new[4] = N2[3];  N2new[4] = N2[5];
749   N1new[5] = N1[5];  N2new[5] = N1[4];
750   // change nodes in faces
751   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753
754   // move the central node of biquadratic triangle
755   SMESH_MesherHelper helper( *GetMesh() );
756   for ( int is2nd = 0; is2nd < 2; ++is2nd )
757   {
758     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
759     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760     if ( nodes.size() < 7 )
761       continue;
762     helper.SetSubShape( tria->getshapeId() );
763     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
764     gp_Pnt xyz;
765     if ( F.IsNull() )
766     {
767       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768               SMESH_TNodeXYZ( nodes[4] ) +
769               SMESH_TNodeXYZ( nodes[5] )) / 3.;
770     }
771     else
772     {
773       bool checkUV;
774       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777       TopLoc_Location loc;
778       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779       xyz = S->Value( uv.X(), uv.Y() );
780       xyz.Transform( loc );
781       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
782            nodes[6]->getshapeId() > 0 )
783         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784     }
785     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
786   }
787   return true;
788 }
789
790 //=======================================================================
791 //function : findTriangles
792 //purpose  : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
794
795 static bool findTriangles(const SMDS_MeshNode *    theNode1,
796                           const SMDS_MeshNode *    theNode2,
797                           const SMDS_MeshElement*& theTria1,
798                           const SMDS_MeshElement*& theTria2)
799 {
800   if ( !theNode1 || !theNode2 ) return false;
801
802   theTria1 = theTria2 = 0;
803
804   set< const SMDS_MeshElement* > emap;
805   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806   while (it->more()) {
807     const SMDS_MeshElement* elem = it->next();
808     if ( elem->NbCornerNodes() == 3 )
809       emap.insert( elem );
810   }
811   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812   while (it->more()) {
813     const SMDS_MeshElement* elem = it->next();
814     if ( emap.count( elem )) {
815       if ( !theTria1 )
816       {
817         theTria1 = elem;
818       }
819       else  
820       {
821         theTria2 = elem;
822         // theTria1 must be element with minimum ID
823         if ( theTria2->GetID() < theTria1->GetID() )
824           std::swap( theTria2, theTria1 );
825         return true;
826       }
827     }
828   }
829   return false;
830 }
831
832 //=======================================================================
833 //function : InverseDiag
834 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
835 //           with ones built on the same 4 nodes but having other common link.
836 //           Return false if proper faces not found
837 //=======================================================================
838
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840                                     const SMDS_MeshNode * theNode2)
841 {
842   myLastCreatedElems.Clear();
843   myLastCreatedNodes.Clear();
844
845   MESSAGE( "::InverseDiag()" );
846
847   const SMDS_MeshElement *tr1, *tr2;
848   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849     return false;
850
851   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852   if (!F1) return false;
853   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854   if (!F2) return false;
855   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857
858     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
860     //    |/ |                                    | \|
861     //  B +--+ 2                                B +--+ 2
862
863     // put nodes in array
864     // and find indices of 1,2 and of A in tr1 and of B in tr2
865     int i, iA1 = 0, i1 = 0;
866     const SMDS_MeshNode* aNodes1 [3];
867     SMDS_ElemIteratorPtr it;
868     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870       if ( aNodes1[ i ] == theNode1 )
871         iA1 = i; // node A in tr1
872       else if ( aNodes1[ i ] != theNode2 )
873         i1 = i;  // node 1
874     }
875     int iB2 = 0, i2 = 0;
876     const SMDS_MeshNode* aNodes2 [3];
877     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879       if ( aNodes2[ i ] == theNode2 )
880         iB2 = i; // node B in tr2
881       else if ( aNodes2[ i ] != theNode1 )
882         i2 = i;  // node 2
883     }
884
885     // nodes 1 and 2 should not be the same
886     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
887       return false;
888
889     // tr1: A->2
890     aNodes1[ iA1 ] = aNodes2[ i2 ];
891     // tr2: B->1
892     aNodes2[ iB2 ] = aNodes1[ i1 ];
893
894     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
896
897     return true;
898   }
899
900   // check case of quadratic faces
901   return InverseDiag(tr1,tr2);
902 }
903
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
907 //           fusion of triangles tr1 and tr2 having shared link on
908 //           theNode1 and theNode2
909 //=======================================================================
910
911 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
912                         const SMDS_MeshNode *    theNode1,
913                         const SMDS_MeshNode *    theNode2,
914                         const SMDS_MeshElement * tr1,
915                         const SMDS_MeshElement * tr2 )
916 {
917   if( tr1->NbNodes() != tr2->NbNodes() )
918     return false;
919   // find the 4-th node to insert into tr1
920   const SMDS_MeshNode* n4 = 0;
921   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922   int i=0;
923   while ( !n4 && i<3 ) {
924     const SMDS_MeshNode * n = cast2Node( it->next() );
925     i++;
926     bool isDiag = ( n == theNode1 || n == theNode2 );
927     if ( !isDiag )
928       n4 = n;
929   }
930   // Make an array of nodes to be in a quadrangle
931   int iNode = 0, iFirstDiag = -1;
932   it = tr1->nodesIterator();
933   i=0;
934   while ( i<3 ) {
935     const SMDS_MeshNode * n = cast2Node( it->next() );
936     i++;
937     bool isDiag = ( n == theNode1 || n == theNode2 );
938     if ( isDiag ) {
939       if ( iFirstDiag < 0 )
940         iFirstDiag = iNode;
941       else if ( iNode - iFirstDiag == 1 )
942         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943     }
944     else if ( n == n4 ) {
945       return false; // tr1 and tr2 should not have all the same nodes
946     }
947     theQuadNodes[ iNode++ ] = n;
948   }
949   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950     theQuadNodes[ iNode ] = n4;
951
952   return true;
953 }
954
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
958 //           with a quadrangle built on the same 4 nodes.
959 //           Return false if proper faces not found
960 //=======================================================================
961
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963                                    const SMDS_MeshNode * theNode2)
964 {
965   myLastCreatedElems.Clear();
966   myLastCreatedNodes.Clear();
967
968   MESSAGE( "::DeleteDiag()" );
969
970   const SMDS_MeshElement *tr1, *tr2;
971   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972     return false;
973
974   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975   if (!F1) return false;
976   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977   if (!F2) return false;
978   SMESHDS_Mesh * aMesh = GetMeshDS();
979
980   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982
983     const SMDS_MeshNode* aNodes [ 4 ];
984     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985       return false;
986
987     const SMDS_MeshElement* newElem = 0;
988     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989     myLastCreatedElems.Append(newElem);
990     AddToSameGroups( newElem, tr1, aMesh );
991     int aShapeId = tr1->getshapeId();
992     if ( aShapeId )
993       {
994         aMesh->SetMeshElementOnShape( newElem, aShapeId );
995       }
996     aMesh->RemoveElement( tr1 );
997     aMesh->RemoveElement( tr2 );
998
999     return true;
1000   }
1001
1002   // check case of quadratic faces
1003   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1006     return false;
1007
1008   //       5
1009   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1011   //    |   / |
1012   //  7 +  +  + 6
1013   //    | /9  |
1014   //    |/    |
1015   //  4 +--+--+ 3
1016   //       8
1017
1018   vector< const SMDS_MeshNode* > N1;
1019   vector< const SMDS_MeshNode* > N2;
1020   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021     return false;
1022   // now we receive following N1 and N2 (using numeration as above image)
1023   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1024   // i.e. first nodes from both arrays determ new diagonal
1025
1026   const SMDS_MeshNode* aNodes[8];
1027   aNodes[0] = N1[0];
1028   aNodes[1] = N1[1];
1029   aNodes[2] = N2[0];
1030   aNodes[3] = N2[1];
1031   aNodes[4] = N1[3];
1032   aNodes[5] = N2[5];
1033   aNodes[6] = N2[3];
1034   aNodes[7] = N1[5];
1035
1036   const SMDS_MeshElement* newElem = 0;
1037   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039   myLastCreatedElems.Append(newElem);
1040   AddToSameGroups( newElem, tr1, aMesh );
1041   int aShapeId = tr1->getshapeId();
1042   if ( aShapeId )
1043     {
1044       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045     }
1046   aMesh->RemoveElement( tr1 );
1047   aMesh->RemoveElement( tr2 );
1048
1049   // remove middle node (9)
1050   GetMeshDS()->RemoveNode( N1[4] );
1051
1052   return true;
1053 }
1054
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose  : Reverse theElement orientation
1058 //=======================================================================
1059
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 {
1062   MESSAGE("Reorient");
1063   myLastCreatedElems.Clear();
1064   myLastCreatedNodes.Clear();
1065
1066   if (!theElem)
1067     return false;
1068   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069   if ( !it || !it->more() )
1070     return false;
1071
1072   const SMDSAbs_ElementType type = theElem->GetType();
1073   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074     return false;
1075
1076   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078   {
1079     const SMDS_VtkVolume* aPolyedre =
1080       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081     if (!aPolyedre) {
1082       MESSAGE("Warning: bad volumic element");
1083       return false;
1084     }
1085     const int nbFaces = aPolyedre->NbFaces();
1086     vector<const SMDS_MeshNode *> poly_nodes;
1087     vector<int> quantities (nbFaces);
1088
1089     // reverse each face of the polyedre
1090     for (int iface = 1; iface <= nbFaces; iface++) {
1091       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092       quantities[iface - 1] = nbFaceNodes;
1093
1094       for (inode = nbFaceNodes; inode >= 1; inode--) {
1095         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096         poly_nodes.push_back(curNode);
1097       }
1098     }
1099     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100   }
1101   else // other elements
1102   {
1103     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105     if ( interlace.empty() )
1106     {
1107       std::reverse( nodes.begin(), nodes.end() ); // polygon
1108     }
1109     else if ( interlace.size() > 1 )
1110     {
1111       SMDS_MeshCell::applyInterlace( interlace, nodes );
1112     }
1113     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1114   }
1115   return false;
1116 }
1117
1118 //================================================================================
1119 /*!
1120  * \brief Reorient faces.
1121  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122  * \param theDirection - desired direction of normal of \a theFace
1123  * \param theFace - one of \a theFaces that sould be oriented according to
1124  *        \a theDirection and whose orientation defines orientation of other faces
1125  * \return number of reoriented faces.
1126  */
1127 //================================================================================
1128
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1130                                   const gp_Dir&            theDirection,
1131                                   const SMDS_MeshElement * theFace)
1132 {
1133   int nbReori = 0;
1134   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135
1136   if ( theFaces.empty() )
1137   {
1138     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139     while ( fIt->more() )
1140       theFaces.insert( theFaces.end(), fIt->next() );
1141   }
1142
1143   // orient theFace according to theDirection
1144   gp_XYZ normal;
1145   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146   if ( normal * theDirection.XYZ() < 0 )
1147     nbReori += Reorient( theFace );
1148
1149   // Orient other faces
1150
1151   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152   TIDSortedElemSet avoidSet;
1153   set< SMESH_TLink > checkedLinks;
1154   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155
1156   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157     theFaces.erase( theFace );
1158   startFaces.insert( theFace );
1159
1160   int nodeInd1, nodeInd2;
1161   const SMDS_MeshElement*           otherFace;
1162   vector< const SMDS_MeshElement* > facesNearLink;
1163   vector< std::pair< int, int > >   nodeIndsOfFace;
1164
1165   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166   while ( !startFaces.empty() )
1167   {
1168     startFace = startFaces.begin();
1169     theFace = *startFace;
1170     startFaces.erase( startFace );
1171     if ( !visitedFaces.insert( theFace ).second )
1172       continue;
1173
1174     avoidSet.clear();
1175     avoidSet.insert(theFace);
1176
1177     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178
1179     const int nbNodes = theFace->NbCornerNodes();
1180     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181     {
1182       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183       linkIt_isNew = checkedLinks.insert( link );
1184       if ( !linkIt_isNew.second )
1185       {
1186         // link has already been checked and won't be encountered more
1187         // if the group (theFaces) is manifold
1188         //checkedLinks.erase( linkIt_isNew.first );
1189       }
1190       else
1191       {
1192         facesNearLink.clear();
1193         nodeIndsOfFace.clear();
1194         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195                                                              theFaces, avoidSet,
1196                                                              &nodeInd1, &nodeInd2 )))
1197           if ( otherFace != theFace)
1198           {
1199             facesNearLink.push_back( otherFace );
1200             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201             avoidSet.insert( otherFace );
1202           }
1203         if ( facesNearLink.size() > 1 )
1204         {
1205           // NON-MANIFOLD mesh shell !
1206           // select a face most co-directed with theFace,
1207           // other faces won't be visited this time
1208           gp_XYZ NF, NOF;
1209           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210           double proj, maxProj = -1;
1211           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213             if (( proj = Abs( NF * NOF )) > maxProj ) {
1214               maxProj = proj;
1215               otherFace = facesNearLink[i];
1216               nodeInd1  = nodeIndsOfFace[i].first;
1217               nodeInd2  = nodeIndsOfFace[i].second;
1218             }
1219           }
1220           // not to visit rejected faces
1221           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223               visitedFaces.insert( facesNearLink[i] );
1224         }
1225         else if ( facesNearLink.size() == 1 )
1226         {
1227           otherFace = facesNearLink[0];
1228           nodeInd1  = nodeIndsOfFace.back().first;
1229           nodeInd2  = nodeIndsOfFace.back().second;
1230         }
1231         if ( otherFace && otherFace != theFace)
1232         {
1233           // link must be reverse in otherFace if orientation ot otherFace
1234           // is same as that of theFace
1235           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236           {
1237             nbReori += Reorient( otherFace );
1238           }
1239           startFaces.insert( otherFace );
1240         }
1241       }
1242       std::swap( link.first, link.second ); // reverse the link
1243     }
1244   }
1245   return nbReori;
1246 }
1247
1248 //=======================================================================
1249 //function : getBadRate
1250 //purpose  :
1251 //=======================================================================
1252
1253 static double getBadRate (const SMDS_MeshElement*               theElem,
1254                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1255 {
1256   SMESH::Controls::TSequenceOfXYZ P;
1257   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1258     return 1e100;
1259   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 }
1262
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose  : Cut quadrangles into triangles.
1266 //           theCrit is used to select a diagonal to cut
1267 //=======================================================================
1268
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1270                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1271 {
1272   myLastCreatedElems.Clear();
1273   myLastCreatedNodes.Clear();
1274
1275   if ( !theCrit.get() )
1276     return false;
1277
1278   SMESHDS_Mesh * aMesh = GetMeshDS();
1279
1280   Handle(Geom_Surface) surface;
1281   SMESH_MesherHelper   helper( *GetMesh() );
1282
1283   TIDSortedElemSet::iterator itElem;
1284   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1285   {
1286     const SMDS_MeshElement* elem = *itElem;
1287     if ( !elem || elem->GetType() != SMDSAbs_Face )
1288       continue;
1289     if ( elem->NbCornerNodes() != 4 )
1290       continue;
1291
1292     // retrieve element nodes
1293     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1294
1295     // compare two sets of possible triangles
1296     double aBadRate1, aBadRate2; // to what extent a set is bad
1297     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1300
1301     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1304
1305     const int aShapeId = FindShape( elem );
1306     const SMDS_MeshElement* newElem1 = 0;
1307     const SMDS_MeshElement* newElem2 = 0;
1308
1309     if ( !elem->IsQuadratic() ) // split liner quadrangle
1310     {
1311       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313       if ( aBadRate1 <= aBadRate2 ) {
1314         // tr1 + tr2 is better
1315         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317       }
1318       else {
1319         // tr3 + tr4 is better
1320         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322       }
1323     }
1324     else // split quadratic quadrangle
1325     {
1326       helper.SetIsQuadratic( true );
1327       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1328
1329       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330       if ( aNodes.size() == 9 )
1331       {
1332         helper.SetIsBiQuadratic( true );
1333         if ( aBadRate1 <= aBadRate2 )
1334           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1335         else
1336           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1337       }
1338       // create a new element
1339       if ( aBadRate1 <= aBadRate2 ) {
1340         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342       }
1343       else {
1344         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1346       }
1347     } // quadratic case
1348
1349     // care of a new element
1350
1351     myLastCreatedElems.Append(newElem1);
1352     myLastCreatedElems.Append(newElem2);
1353     AddToSameGroups( newElem1, elem, aMesh );
1354     AddToSameGroups( newElem2, elem, aMesh );
1355
1356     // put a new triangle on the same shape
1357     if ( aShapeId )
1358       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1360
1361     aMesh->RemoveElement( elem );
1362   }
1363   return true;
1364 }
1365
1366 //=======================================================================
1367 /*!
1368  * \brief Split each of given quadrangles into 4 triangles.
1369  * \param theElems - The faces to be splitted. If empty all faces are split.
1370  */
1371 //=======================================================================
1372
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1374 {
1375   myLastCreatedElems.Clear();
1376   myLastCreatedNodes.Clear();
1377
1378   SMESH_MesherHelper helper( *GetMesh() );
1379   helper.SetElementsOnShape( true );
1380
1381   SMDS_ElemIteratorPtr faceIt;
1382   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383   else                    faceIt = elemSetIterator( theElems );
1384
1385   bool   checkUV;
1386   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1387   gp_XYZ xyz[9];
1388   vector< const SMDS_MeshNode* > nodes;
1389   SMESHDS_SubMesh*               subMeshDS;
1390   TopoDS_Face                    F;
1391   Handle(Geom_Surface)           surface;
1392   TopLoc_Location                loc;
1393
1394   while ( faceIt->more() )
1395   {
1396     const SMDS_MeshElement* quad = faceIt->next();
1397     if ( !quad || quad->NbCornerNodes() != 4 )
1398       continue;
1399
1400     // get a surface the quad is on
1401
1402     if ( quad->getshapeId() < 1 )
1403     {
1404       F.Nullify();
1405       helper.SetSubShape( 0 );
1406       subMeshDS = 0;
1407     }
1408     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1409     {
1410       helper.SetSubShape( quad->getshapeId() );
1411       if ( !helper.GetSubShape().IsNull() &&
1412            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1413       {
1414         F = TopoDS::Face( helper.GetSubShape() );
1415         surface = BRep_Tool::Surface( F, loc );
1416         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1417       }
1418       else
1419       {
1420         helper.SetSubShape( 0 );
1421         subMeshDS = 0;
1422       }
1423     }
1424
1425     // create a central node
1426
1427     const SMDS_MeshNode* nCentral;
1428     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1429
1430     if ( nodes.size() == 9 )
1431     {
1432       nCentral = nodes.back();
1433     }
1434     else
1435     {
1436       size_t iN = 0;
1437       if ( F.IsNull() )
1438       {
1439         for ( ; iN < nodes.size(); ++iN )
1440           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1441
1442         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1444
1445         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446                                    xyz[0], xyz[1], xyz[2], xyz[3],
1447                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1448       }
1449       else
1450       {
1451         for ( ; iN < nodes.size(); ++iN )
1452           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1453
1454         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1456
1457         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458                                   uv[0], uv[1], uv[2], uv[3],
1459                                   uv[4], uv[5], uv[6], uv[7] );
1460
1461         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1462         xyz[ 8 ] = p.XYZ();
1463       }
1464
1465       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466                                  uv[8].X(), uv[8].Y() );
1467       myLastCreatedNodes.Append( nCentral );
1468     }
1469
1470     // create 4 triangles
1471
1472     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1473     
1474     helper.SetIsQuadratic  ( nodes.size() > 4 );
1475     helper.SetIsBiQuadratic( nodes.size() == 9 );
1476     if ( helper.GetIsQuadratic() )
1477       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1478
1479     for ( int i = 0; i < 4; ++i )
1480     {
1481       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482                                                nodes[(i+1)%4],
1483                                                nCentral );
1484       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485       myLastCreatedElems.Append( tria );
1486     }
1487   }
1488 }
1489
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose  : Find better diagonal for cutting.
1493 //=======================================================================
1494
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1496                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1497 {
1498   myLastCreatedElems.Clear();
1499   myLastCreatedNodes.Clear();
1500
1501   if (!theCrit.get())
1502     return -1;
1503
1504   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505     return -1;
1506
1507   if( theQuad->NbNodes()==4 ||
1508       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1509
1510     // retrieve element nodes
1511     const SMDS_MeshNode* aNodes [4];
1512     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1513     int i = 0;
1514     //while (itN->more())
1515     while (i<4) {
1516       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1517     }
1518     // compare two sets of possible triangles
1519     double aBadRate1, aBadRate2; // to what extent a set is bad
1520     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1523
1524     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530       return 1; // diagonal 1-3
1531
1532     return 2; // diagonal 2-4
1533   }
1534   return -1;
1535 }
1536
1537 namespace
1538 {
1539   // Methods of splitting volumes into tetra
1540
1541   const int theHexTo5_1[5*4+1] =
1542     {
1543       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1544     };
1545   const int theHexTo5_2[5*4+1] =
1546     {
1547       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1548     };
1549   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1550
1551   const int theHexTo6_1[6*4+1] =
1552     {
1553       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1554     };
1555   const int theHexTo6_2[6*4+1] =
1556     {
1557       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1558     };
1559   const int theHexTo6_3[6*4+1] =
1560     {
1561       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1562     };
1563   const int theHexTo6_4[6*4+1] =
1564     {
1565       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1566     };
1567   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1568
1569   const int thePyraTo2_1[2*4+1] =
1570     {
1571       0, 1, 2, 4,    0, 2, 3, 4,   -1
1572     };
1573   const int thePyraTo2_2[2*4+1] =
1574     {
1575       1, 2, 3, 4,    1, 3, 0, 4,   -1
1576     };
1577   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1578
1579   const int thePentaTo3_1[3*4+1] =
1580     {
1581       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1582     };
1583   const int thePentaTo3_2[3*4+1] =
1584     {
1585       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1586     };
1587   const int thePentaTo3_3[3*4+1] =
1588     {
1589       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1590     };
1591   const int thePentaTo3_4[3*4+1] =
1592     {
1593       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1594     };
1595   const int thePentaTo3_5[3*4+1] =
1596     {
1597       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1598     };
1599   const int thePentaTo3_6[3*4+1] =
1600     {
1601       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1602     };
1603   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1605
1606   // Methods of splitting hexahedron into prisms
1607
1608   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1609     {
1610       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1611     };
1612   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1613     {
1614       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1615     };
1616   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1617     {
1618       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1619     };
1620
1621   const int theHexTo2Prisms_BT_1[6*2+1] =
1622     {
1623       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1624     };
1625   const int theHexTo2Prisms_BT_2[6*2+1] =
1626     {
1627       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1628     };
1629   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1630
1631   const int theHexTo2Prisms_LR_1[6*2+1] =
1632     {
1633       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1634     };
1635   const int theHexTo2Prisms_LR_2[6*2+1] =
1636     {
1637       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1638     };
1639   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1640
1641   const int theHexTo2Prisms_FB_1[6*2+1] =
1642     {
1643       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1644     };
1645   const int theHexTo2Prisms_FB_2[6*2+1] =
1646     {
1647       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1648     };
1649   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1650
1651
1652   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1653   {
1654     int _n1, _n2, _n3;
1655     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1658                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1659   };
1660   struct TSplitMethod
1661   {
1662     int        _nbSplits;
1663     int        _nbCorners;
1664     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1666     bool       _ownConn;      //!< to delete _connectivity in destructor
1667     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1668
1669     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672     bool hasFacet( const TTriangleFacet& facet ) const
1673     {
1674       if ( _nbCorners == 4 )
1675       {
1676         const int* tetConn = _connectivity;
1677         for ( ; tetConn[0] >= 0; tetConn += 4 )
1678           if (( facet.contains( tetConn[0] ) +
1679                 facet.contains( tetConn[1] ) +
1680                 facet.contains( tetConn[2] ) +
1681                 facet.contains( tetConn[3] )) == 3 )
1682             return true;
1683       }
1684       else // prism, _nbCorners == 6
1685       {
1686         const int* prismConn = _connectivity;
1687         for ( ; prismConn[0] >= 0; prismConn += 6 )
1688         {
1689           if (( facet.contains( prismConn[0] ) &&
1690                 facet.contains( prismConn[1] ) &&
1691                 facet.contains( prismConn[2] ))
1692               ||
1693               ( facet.contains( prismConn[3] ) &&
1694                 facet.contains( prismConn[4] ) &&
1695                 facet.contains( prismConn[5] )))
1696             return true;
1697         }
1698       }
1699       return false;
1700     }
1701   };
1702
1703   //=======================================================================
1704   /*!
1705    * \brief return TSplitMethod for the given element to split into tetrahedra
1706    */
1707   //=======================================================================
1708
1709   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1710   {
1711     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1712
1713     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714     // an edge and a face barycenter; tertaherdons are based on triangles and
1715     // a volume barycenter
1716     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1717
1718     // Find out how adjacent volumes are split
1719
1720     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1723     {
1724       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726       if ( nbNodes < 4 ) continue;
1727
1728       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729       const int* nInd = vol.GetFaceNodesIndices( iF );
1730       if ( nbNodes == 4 )
1731       {
1732         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1736       }
1737       else
1738       {
1739         int iCom = 0; // common node of triangle faces to split into
1740         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1741         {
1742           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1743                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1744                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1746                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1747                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1749           {
1750             triaSplits.push_back( t012 );
1751             triaSplits.push_back( t023 );
1752             break;
1753           }
1754         }
1755       }
1756       if ( !triaSplits.empty() )
1757         hasAdjacentSplits = true;
1758     }
1759
1760     // Among variants of split method select one compliant with adjacent volumes
1761
1762     TSplitMethod method;
1763     if ( !vol.Element()->IsPoly() && !is24TetMode )
1764     {
1765       int nbVariants = 2, nbTet = 0;
1766       const int** connVariants = 0;
1767       switch ( vol.Element()->GetEntityType() )
1768       {
1769       case SMDSEntity_Hexa:
1770       case SMDSEntity_Quad_Hexa:
1771       case SMDSEntity_TriQuad_Hexa:
1772         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773           connVariants = theHexTo5, nbTet = 5;
1774         else
1775           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1776         break;
1777       case SMDSEntity_Pyramid:
1778       case SMDSEntity_Quad_Pyramid:
1779         connVariants = thePyraTo2;  nbTet = 2;
1780         break;
1781       case SMDSEntity_Penta:
1782       case SMDSEntity_Quad_Penta:
1783         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1784         break;
1785       default:
1786         nbVariants = 0;
1787       }
1788       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1789       {
1790         // check method compliancy with adjacent tetras,
1791         // all found splits must be among facets of tetras described by this method
1792         method = TSplitMethod( nbTet, connVariants[variant] );
1793         if ( hasAdjacentSplits && method._nbSplits > 0 )
1794         {
1795           bool facetCreated = true;
1796           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1797           {
1798             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800               facetCreated = method.hasFacet( *facet );
1801           }
1802           if ( !facetCreated )
1803             method = TSplitMethod(0); // incompatible method
1804         }
1805       }
1806     }
1807     if ( method._nbSplits < 1 )
1808     {
1809       // No standard method is applicable, use a generic solution:
1810       // each facet of a volume is split into triangles and
1811       // each of triangles and a volume barycenter form a tetrahedron.
1812
1813       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1814
1815       int* connectivity = new int[ maxTetConnSize + 1 ];
1816       method._connectivity = connectivity;
1817       method._ownConn = true;
1818       method._baryNode = !isHex27; // to create central node or not
1819
1820       int connSize = 0;
1821       int baryCenInd = vol.NbNodes() - int( isHex27 );
1822       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1823       {
1824         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825         const int*   nInd = vol.GetFaceNodesIndices( iF );
1826         // find common node of triangle facets of tetra to create
1827         int iCommon = 0; // index in linear numeration
1828         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829         if ( !triaSplits.empty() )
1830         {
1831           // by found facets
1832           const TTriangleFacet* facet = &triaSplits.front();
1833           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1836               break;
1837         }
1838         else if ( nbNodes > 3 && !is24TetMode )
1839         {
1840           // find the best method of splitting into triangles by aspect ratio
1841           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842           map< double, int > badness2iCommon;
1843           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1846           {
1847             double badness = 0;
1848             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1849             {
1850               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1851                                       nodes[ iQ*((iLast-1)%nbNodes)],
1852                                       nodes[ iQ*((iLast  )%nbNodes)]);
1853               badness += getBadRate( &tria, aspectRatio );
1854             }
1855             badness2iCommon.insert( make_pair( badness, iCommon ));
1856           }
1857           // use iCommon with lowest badness
1858           iCommon = badness2iCommon.begin()->second;
1859         }
1860         if ( iCommon >= nbNodes )
1861           iCommon = 0; // something wrong
1862
1863         // fill connectivity of tetrahedra based on a current face
1864         int nbTet = nbNodes - 2;
1865         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1866         {
1867           int faceBaryCenInd;
1868           if ( isHex27 )
1869           {
1870             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1872           }
1873           else
1874           {
1875             method._faceBaryNode[ iF ] = 0;
1876             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1877           }
1878           nbTet = nbNodes;
1879           for ( int i = 0; i < nbTet; ++i )
1880           {
1881             int i1 = i, i2 = (i+1) % nbNodes;
1882             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885             connectivity[ connSize++ ] = faceBaryCenInd;
1886             connectivity[ connSize++ ] = baryCenInd;
1887           }
1888         }
1889         else
1890         {
1891           for ( int i = 0; i < nbTet; ++i )
1892           {
1893             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898             connectivity[ connSize++ ] = baryCenInd;
1899           }
1900         }
1901         method._nbSplits += nbTet;
1902
1903       } // loop on volume faces
1904
1905       connectivity[ connSize++ ] = -1;
1906
1907     } // end of generic solution
1908
1909     return method;
1910   }
1911   //=======================================================================
1912   /*!
1913    * \brief return TSplitMethod to split haxhedron into prisms
1914    */
1915   //=======================================================================
1916
1917   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918                                     const int        methodFlags,
1919                                     const int        facetToSplit)
1920   {
1921     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1922     // B, T, L, B, R, F
1923     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1924
1925     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1926     {
1927       static TSplitMethod to4methods[4]; // order BT, LR, FB
1928       if ( to4methods[iF]._nbSplits == 0 )
1929       {
1930         switch ( iF ) {
1931         case 0:
1932           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933           to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934           to4methods[iF]._faceBaryNode[ 1 ] = 0;
1935           break;
1936         case 1:
1937           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938           to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939           to4methods[iF]._faceBaryNode[ 4 ] = 0;
1940           break;
1941         case 2:
1942           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943           to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944           to4methods[iF]._faceBaryNode[ 5 ] = 0;
1945           break;
1946         default: return to4methods[3];
1947         }
1948         to4methods[iF]._nbSplits  = 4;
1949         to4methods[iF]._nbCorners = 6;
1950       }
1951       return to4methods[iF];
1952     }
1953     // else if ( methodFlags == HEXA_TO_2_PRISMS )
1954
1955     TSplitMethod method;
1956
1957     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1958
1959     const int nbVariants = 2, nbSplits = 2;
1960     const int** connVariants = 0;
1961     switch ( iF ) {
1962     case 0: connVariants = theHexTo2Prisms_BT; break;
1963     case 1: connVariants = theHexTo2Prisms_LR; break;
1964     case 2: connVariants = theHexTo2Prisms_FB; break;
1965     default: return method;
1966     }
1967
1968     // look for prisms adjacent via facetToSplit and an opposite one
1969     for ( int is2nd = 0; is2nd < 2; ++is2nd )
1970     {
1971       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973       if ( nbNodes != 4 ) return method;
1974
1975       const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1978       TTriangleFacet* t;
1979       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1980         t = &t012;
1981       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1982         t = &t123;
1983       else
1984         continue;
1985
1986       // there are adjacent prism
1987       for ( int variant = 0; variant < nbVariants; ++variant )
1988       {
1989         // check method compliancy with adjacent prisms,
1990         // the found prism facets must be among facets of prisms described by current method
1991         method._nbSplits     = nbSplits;
1992         method._nbCorners    = 6;
1993         method._connectivity = connVariants[ variant ];
1994         if ( method.hasFacet( *t ))
1995           return method;
1996       }
1997     }
1998
1999     // No adjacent prisms. Select a variant with a best aspect ratio.
2000
2001     double badness[2] = { 0, 0 };
2002     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003     const SMDS_MeshNode** nodes = vol.GetNodes();
2004     for ( int variant = 0; variant < nbVariants; ++variant )
2005       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2006       {
2007         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2009
2010         method._connectivity = connVariants[ variant ];
2011         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2014
2015         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2016                                 nodes[ t->_n2 ],
2017                                 nodes[ t->_n3 ] );
2018         badness[ variant ] += getBadRate( &tria, aspectRatio );
2019       }
2020     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2021
2022     method._nbSplits     = nbSplits;
2023     method._nbCorners    = 6;
2024     method._connectivity = connVariants[ iBetter ];
2025
2026     return method;
2027   }
2028
2029   //================================================================================
2030   /*!
2031    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2032    */
2033   //================================================================================
2034
2035   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2036                                        const SMDSAbs_GeometryType geom ) const
2037   {
2038     // find the tetrahedron including the three nodes of facet
2039     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043     while ( volIt1->more() )
2044     {
2045       const SMDS_MeshElement* v = volIt1->next();
2046       if ( v->GetGeomType() != geom )
2047         continue;
2048       const int lastCornerInd = v->NbCornerNodes() - 1;
2049       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050         continue; // medium node not allowed
2051       const int ind2 = v->GetNodeIndex( n2 );
2052       if ( ind2 < 0 || lastCornerInd < ind2 )
2053         continue;
2054       const int ind3 = v->GetNodeIndex( n3 );
2055       if ( ind3 < 0 || lastCornerInd < ind3 )
2056         continue;
2057       return true;
2058     }
2059     return false;
2060   }
2061
2062   //=======================================================================
2063   /*!
2064    * \brief A key of a face of volume
2065    */
2066   //=======================================================================
2067
2068   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2069   {
2070     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2071     {
2072       TIDSortedNodeSet sortedNodes;
2073       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074       int nbNodes = vol.NbFaceNodes( iF );
2075       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076       for ( int i = 0; i < nbNodes; i += iQ )
2077         sortedNodes.insert( fNodes[i] );
2078       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079       first.first   = (*(n++))->GetID();
2080       first.second  = (*(n++))->GetID();
2081       second.first  = (*(n++))->GetID();
2082       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2083     }
2084   };
2085 } // namespace
2086
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose  : Split volume elements into tetrahedra or prisms.
2090 //           If facet ID < 0, element is split into tetrahedra,
2091 //           else a hexahedron is split into prisms so that the given facet is
2092 //           split into triangles
2093 //=======================================================================
2094
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096                                      const int            theMethodFlags)
2097 {
2098   // std-like iterator on coordinates of nodes of mesh element
2099   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100   NXyzIterator xyzEnd;
2101
2102   SMDS_VolumeTool    volTool;
2103   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104   fHelper.ToFixNodeParameters( true );
2105
2106   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2108
2109   SMESH_SequenceOfElemPtr newNodes, newElems;
2110
2111   // map face of volume to it's baricenrtic node
2112   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2113   double bc[3];
2114
2115   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116   for ( ; elem2facet != theElems.end(); ++elem2facet )
2117   {
2118     const SMDS_MeshElement* elem = elem2facet->first;
2119     const int       facetToSplit = elem2facet->second;
2120     if ( elem->GetType() != SMDSAbs_Volume )
2121       continue;
2122     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2124       continue;
2125
2126     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2127
2128     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2129                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2130                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131     if ( splitMethod._nbSplits < 1 ) continue;
2132
2133     // find submesh to add new tetras to
2134     if ( !subMesh || !subMesh->Contains( elem ))
2135     {
2136       int shapeID = FindShape( elem );
2137       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138       subMesh = GetMeshDS()->MeshElements( shapeID );
2139     }
2140     int iQ;
2141     if ( elem->IsQuadratic() )
2142     {
2143       iQ = 2;
2144       // add quadratic links to the helper
2145       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2146       {
2147         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149         for ( int iN = 0; iN < nbN; iN += iQ )
2150           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2151       }
2152       helper.SetIsQuadratic( true );
2153     }
2154     else
2155     {
2156       iQ = 1;
2157       helper.SetIsQuadratic( false );
2158     }
2159     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160                                         volTool.GetNodes() + elem->NbNodes() );
2161     helper.SetElementsOnShape( true );
2162     if ( splitMethod._baryNode )
2163     {
2164       // make a node at barycenter
2165       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167       nodes.push_back( gcNode );
2168       newNodes.Append( gcNode );
2169     }
2170     if ( !splitMethod._faceBaryNode.empty() )
2171     {
2172       // make or find baricentric nodes of faces
2173       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2175       {
2176         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177           volFace2BaryNode.insert
2178           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2179         if ( !f_n->second )
2180         {
2181           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2183         }
2184         nodes.push_back( iF_n->second = f_n->second );
2185       }
2186     }
2187
2188     // make new volumes
2189     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190     const int* volConn = splitMethod._connectivity;
2191     if ( splitMethod._nbCorners == 4 ) // tetra
2192       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194                                                             nodes[ volConn[1] ],
2195                                                             nodes[ volConn[2] ],
2196                                                             nodes[ volConn[3] ]));
2197     else // prisms
2198       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200                                                             nodes[ volConn[1] ],
2201                                                             nodes[ volConn[2] ],
2202                                                             nodes[ volConn[3] ],
2203                                                             nodes[ volConn[4] ],
2204                                                             nodes[ volConn[5] ]));
2205
2206     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2207
2208     // Split faces on sides of the split volume
2209
2210     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2212     {
2213       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214       if ( nbNodes < 4 ) continue;
2215
2216       // find an existing face
2217       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220                                                                        /*noMedium=*/false))
2221       {
2222         // make triangles
2223         helper.SetElementsOnShape( false );
2224         vector< const SMDS_MeshElement* > triangles;
2225
2226         // find submesh to add new triangles in
2227         if ( !fSubMesh || !fSubMesh->Contains( face ))
2228         {
2229           int shapeID = FindShape( face );
2230           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2231         }
2232         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233         if ( iF_n != splitMethod._faceBaryNode.end() )
2234         {
2235           const SMDS_MeshNode *baryNode = iF_n->second;
2236           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2237           {
2238             const SMDS_MeshNode* n1 = fNodes[iN];
2239             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240             const SMDS_MeshNode *n3 = baryNode;
2241             if ( !volTool.IsFaceExternal( iF ))
2242               swap( n2, n3 );
2243             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2244           }
2245           if ( fSubMesh ) // update position of the bary node on geometry
2246           {
2247             if ( subMesh )
2248               subMesh->RemoveNode( baryNode, false );
2249             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2252             {
2253               fHelper.SetSubShape( s );
2254               gp_XY uv( 1e100, 1e100 );
2255               double distXYZ[4];
2256               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2258                    uv.X() < 1e100 )
2259               {
2260                 // node is too far from the surface
2261                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2264               }
2265             }
2266           }
2267         }
2268         else
2269         {
2270           // among possible triangles create ones discribed by split method
2271           const int* nInd = volTool.GetFaceNodesIndices( iF );
2272           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273           int iCom = 0; // common node of triangle faces to split into
2274           list< TTriangleFacet > facets;
2275           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2276           {
2277             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2278                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2279                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2281                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2282                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2284             {
2285               facets.push_back( t012 );
2286               facets.push_back( t023 );
2287               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2289                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2290                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2291               break;
2292             }
2293           }
2294           list< TTriangleFacet >::iterator facet = facets.begin();
2295           if ( facet == facets.end() )
2296             break;
2297           for ( ; facet != facets.end(); ++facet )
2298           {
2299             if ( !volTool.IsFaceExternal( iF ))
2300               swap( facet->_n2, facet->_n3 );
2301             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302                                                  volNodes[ facet->_n2 ],
2303                                                  volNodes[ facet->_n3 ]));
2304           }
2305         }
2306         for ( int i = 0; i < triangles.size(); ++i )
2307         {
2308           if ( !triangles[i] ) continue;
2309           if ( fSubMesh )
2310             fSubMesh->AddElement( triangles[i]);
2311           newElems.Append( triangles[i] );
2312         }
2313         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2315
2316       } // while a face based on facet nodes exists
2317     } // loop on volume faces to split them into triangles
2318
2319     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2320
2321     if ( geomType == SMDSEntity_TriQuad_Hexa )
2322     {
2323       // remove medium nodes that could become free
2324       for ( int i = 20; i < volTool.NbNodes(); ++i )
2325         if ( volNodes[i]->NbInverseElements() == 0 )
2326           GetMeshDS()->RemoveNode( volNodes[i] );
2327     }
2328   } // loop on volumes to split
2329   
2330   myLastCreatedNodes = newNodes;
2331   myLastCreatedElems = newElems;
2332 }
2333
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose  : For hexahedra that will be split into prisms, finds facets to
2337 //           split into triangles. Only hexahedra adjacent to the one closest
2338 //           to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in]     theFacetNormal - facet normal
2341 //param [out]    theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2343
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345                                              const gp_Ax1&     theFacetNormal,
2346                                              TFacetOfElem &    theFacets)
2347 {
2348   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2349
2350   // Find a hexa closest to the location of theFacetNormal
2351
2352   const SMDS_MeshElement* startHex;
2353   {
2354     // get SMDS_ElemIteratorPtr on theHexas
2355     typedef const SMDS_MeshElement*                                      TValue;
2356     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2357     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2358     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2359     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361       ( new TElemSetIter( theHexas.begin(),
2362                           theHexas.end(),
2363                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2364
2365     SMESH_ElementSearcher* searcher =
2366       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2367
2368     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2369
2370     delete searcher;
2371
2372     if ( !startHex )
2373       throw SALOME_Exception( THIS_METHOD "startHex not found");
2374   }
2375
2376   // Select a facet of startHex by theFacetNormal
2377
2378   SMDS_VolumeTool vTool( startHex );
2379   double norm[3], dot, maxDot = 0;
2380   int facetID = -1;
2381   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2383     {
2384       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2385       if ( dot > maxDot )
2386       {
2387         facetID = iF;
2388         maxDot = dot;
2389       }
2390     }
2391   if ( facetID < 0 )
2392     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2393
2394   // Fill theFacets starting from facetID of startHex
2395
2396   // facets used for seach of volumes adjacent to already treated ones
2397   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2399   TFacetMap facetsToCheck;
2400
2401   set<const SMDS_MeshNode*> facetNodes;
2402   const SMDS_MeshElement*   curHex;
2403
2404   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2405
2406   while ( startHex )
2407   {
2408     // move in two directions from startHex via facetID
2409     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2410     {
2411       curHex       = startHex;
2412       int curFacet = facetID;
2413       if ( is2nd ) // do not treat startHex twice
2414       {
2415         vTool.Set( curHex );
2416         if ( vTool.IsFreeFace( curFacet, &curHex ))
2417         {
2418           curHex = 0;
2419         }
2420         else
2421         {
2422           vTool.GetFaceNodes( curFacet, facetNodes );
2423           vTool.Set( curHex );
2424           curFacet = vTool.GetFaceIndex( facetNodes );
2425         }
2426       }
2427       while ( curHex )
2428       {
2429         // store a facet to split
2430         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2431         {
2432           theFacets.insert( make_pair( curHex, -1 ));
2433           break;
2434         }
2435         if ( !allHex && !theHexas.count( curHex ))
2436           break;
2437
2438         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439           theFacets.insert( make_pair( curHex, curFacet ));
2440         if ( !facetIt2isNew.second )
2441           break;
2442
2443         // remember not-to-split facets in facetsToCheck
2444         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2446         {
2447           if ( iF == curFacet && iF == oppFacet )
2448             continue;
2449           TVolumeFaceKey facetKey ( vTool, iF );
2450           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2451           pair< TFacetMap::iterator, bool > it2isnew =
2452             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453           if ( !it2isnew.second )
2454             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2455         }
2456         // pass to a volume adjacent via oppFacet
2457         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2458         {
2459           curHex = 0;
2460         }
2461         else
2462         {
2463           // get a new curFacet
2464           vTool.GetFaceNodes( oppFacet, facetNodes );
2465           vTool.Set( curHex );
2466           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2467         }
2468       }
2469     } // move in two directions from startHex via facetID
2470
2471     // Find a new startHex by facetsToCheck
2472
2473     startHex = 0;
2474     facetID  = -1;
2475     TFacetMap::iterator fIt = facetsToCheck.begin();
2476     while ( !startHex && fIt != facetsToCheck.end() )
2477     {
2478       const TElemFacets&  elemFacets = fIt->second;
2479       const SMDS_MeshElement*    hex = elemFacets.first->first;
2480       int                 splitFacet = elemFacets.first->second;
2481       int               lateralFacet = elemFacets.second;
2482       facetsToCheck.erase( fIt );
2483       fIt = facetsToCheck.begin();
2484
2485       vTool.Set( hex );
2486       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2487            curHex->GetGeomType() != SMDSGeom_HEXA )
2488         continue;
2489       if ( !allHex && !theHexas.count( curHex ))
2490         continue;
2491
2492       startHex = curHex;
2493
2494       // find a facet of startHex to split 
2495
2496       set<const SMDS_MeshNode*> lateralNodes;
2497       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498       vTool.GetFaceNodes( splitFacet,   facetNodes );
2499       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500       vTool.Set( startHex );
2501       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2502
2503       // look for a facet of startHex having common nodes with facetNodes
2504       // but not lateralFacet
2505       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2506       {
2507         if ( iF == lateralFacet )
2508           continue;
2509         int nbCommonNodes = 0;
2510         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512           nbCommonNodes += facetNodes.count( nn[ iN ]);
2513
2514         if ( nbCommonNodes >= 2 )
2515         {
2516           facetID = iF;
2517           break;
2518         }
2519       }
2520       if ( facetID < 0 )
2521         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2522     }
2523   } //   while ( startHex )
2524 }
2525
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2530
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532                                         const SMDS_MeshElement* elemInGroups,
2533                                         SMESHDS_Mesh *          aMesh)
2534 {
2535   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536   if (!groups.empty()) {
2537     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538     for ( ; grIt != groups.end(); grIt++ ) {
2539       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540       if ( group && group->Contains( elemInGroups ))
2541         group->SMDSGroup().Add( elemToAdd );
2542     }
2543   }
2544 }
2545
2546
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552                                              SMESHDS_Mesh *          aMesh)
2553 {
2554   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555   if (!groups.empty())
2556   {
2557     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558     for (; GrIt != groups.end(); GrIt++)
2559     {
2560       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561       if (!grp || grp->IsEmpty()) continue;
2562       grp->SMDSGroup().Remove(removeelem);
2563     }
2564   }
2565 }
2566
2567 //================================================================================
2568 /*!
2569  * \brief Replace elemToRm by elemToAdd in the all groups
2570  */
2571 //================================================================================
2572
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574                                             const SMDS_MeshElement* elemToAdd,
2575                                             SMESHDS_Mesh *          aMesh)
2576 {
2577   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578   if (!groups.empty()) {
2579     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580     for ( ; grIt != groups.end(); grIt++ ) {
2581       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583         group->SMDSGroup().Add( elemToAdd );
2584     }
2585   }
2586 }
2587
2588 //================================================================================
2589 /*!
2590  * \brief Replace elemToRm by elemToAdd in the all groups
2591  */
2592 //================================================================================
2593
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2595                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2596                                             SMESHDS_Mesh *                         aMesh)
2597 {
2598   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599   if (!groups.empty())
2600   {
2601     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602     for ( ; grIt != groups.end(); grIt++ ) {
2603       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605         for ( int i = 0; i < elemToAdd.size(); ++i )
2606           group->SMDSGroup().Add( elemToAdd[ i ] );
2607     }
2608   }
2609 }
2610
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose  : Cut quadrangles into triangles.
2614 //           theCrit is used to select a diagonal to cut
2615 //=======================================================================
2616
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618                                   const bool         the13Diag)
2619 {
2620   myLastCreatedElems.Clear();
2621   myLastCreatedNodes.Clear();
2622
2623   MESSAGE( "::QuadToTri()" );
2624
2625   SMESHDS_Mesh * aMesh = GetMeshDS();
2626
2627   Handle(Geom_Surface) surface;
2628   SMESH_MesherHelper   helper( *GetMesh() );
2629
2630   TIDSortedElemSet::iterator itElem;
2631   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632     const SMDS_MeshElement* elem = *itElem;
2633     if ( !elem || elem->GetType() != SMDSAbs_Face )
2634       continue;
2635     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636     if(!isquad) continue;
2637
2638     if(elem->NbNodes()==4) {
2639       // retrieve element nodes
2640       const SMDS_MeshNode* aNodes [4];
2641       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2642       int i = 0;
2643       while ( itN->more() )
2644         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2645
2646       int aShapeId = FindShape( elem );
2647       const SMDS_MeshElement* newElem1 = 0;
2648       const SMDS_MeshElement* newElem2 = 0;
2649       if ( the13Diag ) {
2650         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2652       }
2653       else {
2654         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2656       }
2657       myLastCreatedElems.Append(newElem1);
2658       myLastCreatedElems.Append(newElem2);
2659       // put a new triangle on the same shape and add to the same groups
2660       if ( aShapeId )
2661         {
2662           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2664         }
2665       AddToSameGroups( newElem1, elem, aMesh );
2666       AddToSameGroups( newElem2, elem, aMesh );
2667       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668       aMesh->RemoveElement( elem );
2669     }
2670
2671     // Quadratic quadrangle
2672
2673     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2674
2675       // get surface elem is on
2676       int aShapeId = FindShape( elem );
2677       if ( aShapeId != helper.GetSubShapeID() ) {
2678         surface.Nullify();
2679         TopoDS_Shape shape;
2680         if ( aShapeId > 0 )
2681           shape = aMesh->IndexToShape( aShapeId );
2682         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683           TopoDS_Face face = TopoDS::Face( shape );
2684           surface = BRep_Tool::Surface( face );
2685           if ( !surface.IsNull() )
2686             helper.SetSubShape( shape );
2687         }
2688       }
2689
2690       const SMDS_MeshNode* aNodes [8];
2691       const SMDS_MeshNode* inFaceNode = 0;
2692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2693       int i = 0;
2694       while ( itN->more() ) {
2695         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2698         {
2699           inFaceNode = aNodes[ i-1 ];
2700         }
2701       }
2702
2703       // find middle point for (0,1,2,3)
2704       // and create a node in this point;
2705       gp_XYZ p( 0,0,0 );
2706       if ( surface.IsNull() ) {
2707         for(i=0; i<4; i++)
2708           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2709         p /= 4;
2710       }
2711       else {
2712         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2713         gp_XY uv( 0,0 );
2714         for(i=0; i<4; i++)
2715           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2716         uv /= 4.;
2717         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2718       }
2719       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720       myLastCreatedNodes.Append(newN);
2721
2722       // create a new element
2723       const SMDS_MeshElement* newElem1 = 0;
2724       const SMDS_MeshElement* newElem2 = 0;
2725       if ( the13Diag ) {
2726         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727                                   aNodes[6], aNodes[7], newN );
2728         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729                                   newN,      aNodes[4], aNodes[5] );
2730       }
2731       else {
2732         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733                                   aNodes[7], aNodes[4], newN );
2734         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735                                   newN,      aNodes[5], aNodes[6] );
2736       }
2737       myLastCreatedElems.Append(newElem1);
2738       myLastCreatedElems.Append(newElem2);
2739       // put a new triangle on the same shape and add to the same groups
2740       if ( aShapeId )
2741         {
2742           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2744         }
2745       AddToSameGroups( newElem1, elem, aMesh );
2746       AddToSameGroups( newElem2, elem, aMesh );
2747       aMesh->RemoveElement( elem );
2748     }
2749   }
2750
2751   return true;
2752 }
2753
2754 //=======================================================================
2755 //function : getAngle
2756 //purpose  :
2757 //=======================================================================
2758
2759 double getAngle(const SMDS_MeshElement * tr1,
2760                 const SMDS_MeshElement * tr2,
2761                 const SMDS_MeshNode *    n1,
2762                 const SMDS_MeshNode *    n2)
2763 {
2764   double angle = 2. * M_PI; // bad angle
2765
2766   // get normals
2767   SMESH::Controls::TSequenceOfXYZ P1, P2;
2768   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2770     return angle;
2771   gp_Vec N1,N2;
2772   if(!tr1->IsQuadratic())
2773     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2774   else
2775     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776   if ( N1.SquareMagnitude() <= gp::Resolution() )
2777     return angle;
2778   if(!tr2->IsQuadratic())
2779     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2780   else
2781     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782   if ( N2.SquareMagnitude() <= gp::Resolution() )
2783     return angle;
2784
2785   // find the first diagonal node n1 in the triangles:
2786   // take in account a diagonal link orientation
2787   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788   for ( int t = 0; t < 2; t++ ) {
2789     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790     int i = 0, iDiag = -1;
2791     while ( it->more()) {
2792       const SMDS_MeshElement *n = it->next();
2793       if ( n == n1 || n == n2 ) {
2794         if ( iDiag < 0)
2795           iDiag = i;
2796         else {
2797           if ( i - iDiag == 1 )
2798             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2799           else
2800             nFirst[ t ] = n;
2801           break;
2802         }
2803       }
2804       i++;
2805     }
2806   }
2807   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2808     N2.Reverse();
2809
2810   angle = N1.Angle( N2 );
2811   //SCRUTE( angle );
2812   return angle;
2813 }
2814
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2819 class LinkID_Gen {
2820 public:
2821
2822   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2824   {}
2825
2826   long GetLinkID (const SMDS_MeshNode * n1,
2827                   const SMDS_MeshNode * n2) const
2828   {
2829     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2830   }
2831
2832   bool GetNodes (const long             theLinkID,
2833                  const SMDS_MeshNode* & theNode1,
2834                  const SMDS_MeshNode* & theNode2) const
2835   {
2836     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837     if ( !theNode1 ) return false;
2838     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839     if ( !theNode2 ) return false;
2840     return true;
2841   }
2842
2843 private:
2844   LinkID_Gen();
2845   const SMESHDS_Mesh* myMesh;
2846   long                myMaxID;
2847 };
2848
2849
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose  : Fuse neighbour triangles into quadrangles.
2853 //           theCrit is used to select a neighbour to fuse with.
2854 //           theMaxAngle is a max angle between element normals at which
2855 //           fusion is still performed.
2856 //=======================================================================
2857
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2859                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2860                                   const double                         theMaxAngle)
2861 {
2862   myLastCreatedElems.Clear();
2863   myLastCreatedNodes.Clear();
2864
2865   MESSAGE( "::TriToQuad()" );
2866
2867   if ( !theCrit.get() )
2868     return false;
2869
2870   SMESHDS_Mesh * aMesh = GetMeshDS();
2871
2872   // Prepare data for algo: build
2873   // 1. map of elements with their linkIDs
2874   // 2. map of linkIDs with their elements
2875
2876   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2879   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2880
2881   TIDSortedElemSet::iterator itElem;
2882   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2883   {
2884     const SMDS_MeshElement* elem = *itElem;
2885     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886     bool IsTria = ( elem->NbCornerNodes()==3 );
2887     if (!IsTria) continue;
2888
2889     // retrieve element nodes
2890     const SMDS_MeshNode* aNodes [4];
2891     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2892     int i = 0;
2893     while ( i < 3 )
2894       aNodes[ i++ ] = itN->next();
2895     aNodes[ 3 ] = aNodes[ 0 ];
2896
2897     // fill maps
2898     for ( i = 0; i < 3; i++ ) {
2899       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900       // check if elements sharing a link can be fused
2901       itLE = mapLi_listEl.find( link );
2902       if ( itLE != mapLi_listEl.end() ) {
2903         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2904           continue;
2905         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906         //if ( FindShape( elem ) != FindShape( elem2 ))
2907         //  continue; // do not fuse triangles laying on different shapes
2908         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909           continue; // avoid making badly shaped quads
2910         (*itLE).second.push_back( elem );
2911       }
2912       else {
2913         mapLi_listEl[ link ].push_back( elem );
2914       }
2915       mapEl_setLi [ elem ].insert( link );
2916     }
2917   }
2918   // Clean the maps from the links shared by a sole element, ie
2919   // links to which only one element is bound in mapLi_listEl
2920
2921   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922     int nbElems = (*itLE).second.size();
2923     if ( nbElems < 2  ) {
2924       const SMDS_MeshElement* elem = (*itLE).second.front();
2925       SMESH_TLink link = (*itLE).first;
2926       mapEl_setLi[ elem ].erase( link );
2927       if ( mapEl_setLi[ elem ].empty() )
2928         mapEl_setLi.erase( elem );
2929     }
2930   }
2931
2932   // Algo: fuse triangles into quadrangles
2933
2934   while ( ! mapEl_setLi.empty() ) {
2935     // Look for the start element:
2936     // the element having the least nb of shared links
2937     const SMDS_MeshElement* startElem = 0;
2938     int minNbLinks = 4;
2939     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940       int nbLinks = (*itEL).second.size();
2941       if ( nbLinks < minNbLinks ) {
2942         startElem = (*itEL).first;
2943         minNbLinks = nbLinks;
2944         if ( minNbLinks == 1 )
2945           break;
2946       }
2947     }
2948
2949     // search elements to fuse starting from startElem or links of elements
2950     // fused earlyer - startLinks
2951     list< SMESH_TLink > startLinks;
2952     while ( startElem || !startLinks.empty() ) {
2953       while ( !startElem && !startLinks.empty() ) {
2954         // Get an element to start, by a link
2955         SMESH_TLink linkId = startLinks.front();
2956         startLinks.pop_front();
2957         itLE = mapLi_listEl.find( linkId );
2958         if ( itLE != mapLi_listEl.end() ) {
2959           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961           for ( ; itE != listElem.end() ; itE++ )
2962             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2963               startElem = (*itE);
2964           mapLi_listEl.erase( itLE );
2965         }
2966       }
2967
2968       if ( startElem ) {
2969         // Get candidates to be fused
2970         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971         const SMESH_TLink *link12, *link13;
2972         startElem = 0;
2973         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975         ASSERT( !setLi.empty() );
2976         set< SMESH_TLink >::iterator itLi;
2977         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2978         {
2979           const SMESH_TLink & link = (*itLi);
2980           itLE = mapLi_listEl.find( link );
2981           if ( itLE == mapLi_listEl.end() )
2982             continue;
2983
2984           const SMDS_MeshElement* elem = (*itLE).second.front();
2985           if ( elem == tr1 )
2986             elem = (*itLE).second.back();
2987           mapLi_listEl.erase( itLE );
2988           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2989             continue;
2990           if ( tr2 ) {
2991             tr3 = elem;
2992             link13 = &link;
2993           }
2994           else {
2995             tr2 = elem;
2996             link12 = &link;
2997           }
2998
2999           // add other links of elem to list of links to re-start from
3000           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001           set< SMESH_TLink >::iterator it;
3002           for ( it = links.begin(); it != links.end(); it++ ) {
3003             const SMESH_TLink& link2 = (*it);
3004             if ( link2 != link )
3005               startLinks.push_back( link2 );
3006           }
3007         }
3008
3009         // Get nodes of possible quadrangles
3010         const SMDS_MeshNode *n12 [4], *n13 [4];
3011         bool Ok12 = false, Ok13 = false;
3012         const SMDS_MeshNode *linkNode1, *linkNode2;
3013         if(tr2) {
3014           linkNode1 = link12->first;
3015           linkNode2 = link12->second;
3016           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3017             Ok12 = true;
3018         }
3019         if(tr3) {
3020           linkNode1 = link13->first;
3021           linkNode2 = link13->second;
3022           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3023             Ok13 = true;
3024         }
3025
3026         // Choose a pair to fuse
3027         if ( Ok12 && Ok13 ) {
3028           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030           double aBadRate12 = getBadRate( &quad12, theCrit );
3031           double aBadRate13 = getBadRate( &quad13, theCrit );
3032           if (  aBadRate13 < aBadRate12 )
3033             Ok12 = false;
3034           else
3035             Ok13 = false;
3036         }
3037
3038         // Make quadrangles
3039         // and remove fused elems and remove links from the maps
3040         mapEl_setLi.erase( tr1 );
3041         if ( Ok12 )
3042         {
3043           mapEl_setLi.erase( tr2 );
3044           mapLi_listEl.erase( *link12 );
3045           if ( tr1->NbNodes() == 3 )
3046           {
3047             const SMDS_MeshElement* newElem = 0;
3048             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049             myLastCreatedElems.Append(newElem);
3050             AddToSameGroups( newElem, tr1, aMesh );
3051             int aShapeId = tr1->getshapeId();
3052             if ( aShapeId )
3053               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054             aMesh->RemoveElement( tr1 );
3055             aMesh->RemoveElement( tr2 );
3056           }
3057           else {
3058             vector< const SMDS_MeshNode* > N1;
3059             vector< const SMDS_MeshNode* > N2;
3060             getNodesFromTwoTria(tr1,tr2,N1,N2);
3061             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3063             // i.e. first nodes from both arrays form a new diagonal
3064             const SMDS_MeshNode* aNodes[8];
3065             aNodes[0] = N1[0];
3066             aNodes[1] = N1[1];
3067             aNodes[2] = N2[0];
3068             aNodes[3] = N2[1];
3069             aNodes[4] = N1[3];
3070             aNodes[5] = N2[5];
3071             aNodes[6] = N2[3];
3072             aNodes[7] = N1[5];
3073             const SMDS_MeshElement* newElem = 0;
3074             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3077             else
3078               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080             myLastCreatedElems.Append(newElem);
3081             AddToSameGroups( newElem, tr1, aMesh );
3082             int aShapeId = tr1->getshapeId();
3083             if ( aShapeId )
3084               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085             aMesh->RemoveElement( tr1 );
3086             aMesh->RemoveElement( tr2 );
3087             // remove middle node (9)
3088             if ( N1[4]->NbInverseElements() == 0 )
3089               aMesh->RemoveNode( N1[4] );
3090             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091               aMesh->RemoveNode( N1[6] );
3092             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093               aMesh->RemoveNode( N2[6] );
3094           }
3095         }
3096         else if ( Ok13 )
3097         {
3098           mapEl_setLi.erase( tr3 );
3099           mapLi_listEl.erase( *link13 );
3100           if ( tr1->NbNodes() == 3 ) {
3101             const SMDS_MeshElement* newElem = 0;
3102             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103             myLastCreatedElems.Append(newElem);
3104             AddToSameGroups( newElem, tr1, aMesh );
3105             int aShapeId = tr1->getshapeId();
3106             if ( aShapeId )
3107               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108             aMesh->RemoveElement( tr1 );
3109             aMesh->RemoveElement( tr3 );
3110           }
3111           else {
3112             vector< const SMDS_MeshNode* > N1;
3113             vector< const SMDS_MeshNode* > N2;
3114             getNodesFromTwoTria(tr1,tr3,N1,N2);
3115             // now we receive following N1 and N2 (using numeration as above image)
3116             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3117             // i.e. first nodes from both arrays form a new diagonal
3118             const SMDS_MeshNode* aNodes[8];
3119             aNodes[0] = N1[0];
3120             aNodes[1] = N1[1];
3121             aNodes[2] = N2[0];
3122             aNodes[3] = N2[1];
3123             aNodes[4] = N1[3];
3124             aNodes[5] = N2[5];
3125             aNodes[6] = N2[3];
3126             aNodes[7] = N1[5];
3127             const SMDS_MeshElement* newElem = 0;
3128             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3131             else
3132               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134             myLastCreatedElems.Append(newElem);
3135             AddToSameGroups( newElem, tr1, aMesh );
3136             int aShapeId = tr1->getshapeId();
3137             if ( aShapeId )
3138               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139             aMesh->RemoveElement( tr1 );
3140             aMesh->RemoveElement( tr3 );
3141             // remove middle node (9)
3142             if ( N1[4]->NbInverseElements() == 0 )
3143               aMesh->RemoveNode( N1[4] );
3144             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145               aMesh->RemoveNode( N1[6] );
3146             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147               aMesh->RemoveNode( N2[6] );
3148           }
3149         }
3150
3151         // Next element to fuse: the rejected one
3152         if ( tr3 )
3153           startElem = Ok12 ? tr3 : tr2;
3154
3155       } // if ( startElem )
3156     } // while ( startElem || !startLinks.empty() )
3157   } // while ( ! mapEl_setLi.empty() )
3158
3159   return true;
3160 }
3161
3162
3163 /*#define DUMPSO(txt) \
3164 //  cout << txt << endl;
3165 //=============================================================================
3166 //
3167 //
3168 //
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3171 {
3172 if ( i1 == i2 )
3173 return;
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3178 P[ i1 ] = P[ i2 ];
3179 P[ i2 ] = Ptmp;
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3181 }
3182
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3186 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3187 //           1 or 2 else 0.
3188 //=======================================================================
3189
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3191 int               idNodes[] )
3192 {
3193   gp_Pnt P[4];
3194   int i;
3195   for ( i = 0; i < 4; i++ ) {
3196     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3197     if ( !n ) return 0;
3198     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3199   }
3200
3201   gp_Vec V1(P[0], P[1]);
3202   gp_Vec V2(P[0], P[2]);
3203   gp_Vec V3(P[0], P[3]);
3204
3205   gp_Vec Cross1 = V1 ^ V2;
3206   gp_Vec Cross2 = V2 ^ V3;
3207
3208   i = 0;
3209   if (Cross1.Dot(Cross2) < 0)
3210   {
3211     Cross1 = V2 ^ V1;
3212     Cross2 = V1 ^ V3;
3213
3214     if (Cross1.Dot(Cross2) < 0)
3215       i = 2;
3216     else
3217       i = 1;
3218     swap ( i, i + 1, idNodes, P );
3219
3220     //     for ( int ii = 0; ii < 4; ii++ ) {
3221     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3223     //     }
3224   }
3225   return i;
3226 }
3227
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose  : Set 8 nodes of a hexahedron in a good order.
3231 //           Return success status
3232 //=======================================================================
3233
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3235                                       int               idNodes[] )
3236 {
3237   gp_Pnt P[8];
3238   int i;
3239   DUMPSO( "INPUT: ========================================");
3240   for ( i = 0; i < 8; i++ ) {
3241     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242     if ( !n ) return false;
3243     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3245   }
3246   DUMPSO( "========================================");
3247
3248
3249   set<int> faceNodes;  // ids of bottom face nodes, to be found
3250   set<int> checkedId1; // ids of tried 2-nd nodes
3251   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3253   int iMin, iLoop1 = 0;
3254
3255   // Loop to try the 2-nd nodes
3256
3257   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3258   {
3259     // Find not checked 2-nd node
3260     for ( i = 1; i < 8; i++ )
3261       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262         int id1 = idNodes[i];
3263         swap ( 1, i, idNodes, P );
3264         checkedId1.insert ( id1 );
3265         break;
3266       }
3267
3268     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269     // ie that all but meybe one (id3 which is on the same face) nodes
3270     // lay on the same side from the triangle plane.
3271
3272     bool manyInPlane = false; // more than 4 nodes lay in plane
3273     int iLoop2 = 0;
3274     while ( ++iLoop2 < 6 ) {
3275
3276       // get 1-2-3 plane coeffs
3277       Standard_Real A, B, C, D;
3278       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279       if ( N.SquareMagnitude() > gp::Resolution() )
3280       {
3281         gp_Pln pln ( P[0], N );
3282         pln.Coefficients( A, B, C, D );
3283
3284         // find the node (iMin) closest to pln
3285         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3286         set<int> idInPln;
3287         for ( i = 3; i < 8; i++ ) {
3288           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289           if ( fabs( dist[i] ) < minDist ) {
3290             minDist = fabs( dist[i] );
3291             iMin = i;
3292           }
3293           if ( fabs( dist[i] ) <= tol )
3294             idInPln.insert( idNodes[i] );
3295         }
3296
3297         // there should not be more than 4 nodes in bottom plane
3298         if ( idInPln.size() > 1 )
3299         {
3300           DUMPSO( "### idInPln.size() = " << idInPln.size());
3301           // idInPlane does not contain the first 3 nodes
3302           if ( manyInPlane || idInPln.size() == 5)
3303             return false; // all nodes in one plane
3304           manyInPlane = true;
3305
3306           // set the 1-st node to be not in plane
3307           for ( i = 3; i < 8; i++ ) {
3308             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309               DUMPSO( "### Reset 0-th node");
3310               swap( 0, i, idNodes, P );
3311               break;
3312             }
3313           }
3314
3315           // reset to re-check second nodes
3316           leastDist = DBL_MAX;
3317           faceNodes.clear();
3318           checkedId1.clear();
3319           iLoop1 = 0;
3320           break; // from iLoop2;
3321         }
3322
3323         // check that the other 4 nodes are on the same side
3324         bool sameSide = true;
3325         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326         for ( i = 3; sameSide && i < 8; i++ ) {
3327           if ( i != iMin )
3328             sameSide = ( isNeg == dist[i] <= 0.);
3329         }
3330
3331         // keep best solution
3332         if ( sameSide && minDist < leastDist ) {
3333           leastDist = minDist;
3334           faceNodes.clear();
3335           faceNodes.insert( idNodes[ 1 ] );
3336           faceNodes.insert( idNodes[ 2 ] );
3337           faceNodes.insert( idNodes[ iMin ] );
3338           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339                   << " leastDist = " << leastDist);
3340           if ( leastDist <= DBL_MIN )
3341             break;
3342         }
3343       }
3344
3345       // set next 3-d node to check
3346       int iNext = 2 + iLoop2;
3347       if ( iNext < 8 ) {
3348         DUMPSO( "Try 2-nd");
3349         swap ( 2, iNext, idNodes, P );
3350       }
3351     } // while ( iLoop2 < 6 )
3352   } // iLoop1
3353
3354   if ( faceNodes.empty() ) return false;
3355
3356   // Put the faceNodes in proper places
3357   for ( i = 4; i < 8; i++ ) {
3358     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359       // find a place to put
3360       int iTo = 1;
3361       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3362         iTo++;
3363       DUMPSO( "Set faceNodes");
3364       swap ( iTo, i, idNodes, P );
3365     }
3366   }
3367
3368
3369   // Set nodes of the found bottom face in good order
3370   DUMPSO( " Found bottom face: ");
3371   i = SortQuadNodes( theMesh, idNodes );
3372   if ( i ) {
3373     gp_Pnt Ptmp = P[ i ];
3374     P[ i ] = P[ i+1 ];
3375     P[ i+1 ] = Ptmp;
3376   }
3377   //   else
3378   //     for ( int ii = 0; ii < 4; ii++ ) {
3379   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3381   //    }
3382
3383   // Gravity center of the top and bottom faces
3384   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3386
3387   // Get direction from the bottom to the top face
3388   gp_Vec upDir ( aGCb, aGCt );
3389   Standard_Real upDirSize = upDir.Magnitude();
3390   if ( upDirSize <= gp::Resolution() ) return false;
3391   upDir / upDirSize;
3392
3393   // Assure that the bottom face normal points up
3394   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396   if ( Nb.Dot( upDir ) < 0 ) {
3397     DUMPSO( "Reverse bottom face");
3398     swap( 1, 3, idNodes, P );
3399   }
3400
3401   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402   Standard_Real minDist = DBL_MAX;
3403   for ( i = 4; i < 8; i++ ) {
3404     // projection of P[i] to the plane defined by P[0] and upDir
3405     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406     Standard_Real sqDist = P[0].SquareDistance( Pp );
3407     if ( sqDist < minDist ) {
3408       minDist = sqDist;
3409       iMin = i;
3410     }
3411   }
3412   DUMPSO( "Set 4-th");
3413   swap ( 4, iMin, idNodes, P );
3414
3415   // Set nodes of the top face in good order
3416   DUMPSO( "Sort top face");
3417   i = SortQuadNodes( theMesh, &idNodes[4] );
3418   if ( i ) {
3419     i += 4;
3420     gp_Pnt Ptmp = P[ i ];
3421     P[ i ] = P[ i+1 ];
3422     P[ i+1 ] = Ptmp;
3423   }
3424
3425   // Assure that direction of the top face normal is from the bottom face
3426   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428   if ( Nt.Dot( upDir ) < 0 ) {
3429     DUMPSO( "Reverse top face");
3430     swap( 5, 7, idNodes, P );
3431   }
3432
3433   //   DUMPSO( "OUTPUT: ========================================");
3434   //   for ( i = 0; i < 8; i++ ) {
3435   //     float *p = ugrid->GetPoint(idNodes[i]);
3436   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3437   //   }
3438
3439   return true;
3440 }*/
3441
3442 //================================================================================
3443 /*!
3444  * \brief Return nodes linked to the given one
3445  * \param theNode - the node
3446  * \param linkedNodes - the found nodes
3447  * \param type - the type of elements to check
3448  *
3449  * Medium nodes are ignored
3450  */
3451 //================================================================================
3452
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454                                        TIDSortedElemSet &   linkedNodes,
3455                                        SMDSAbs_ElementType  type )
3456 {
3457   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458   while ( elemIt->more() )
3459   {
3460     const SMDS_MeshElement* elem = elemIt->next();
3461     if(elem->GetType() == SMDSAbs_0DElement)
3462       continue;
3463
3464     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465     if ( elem->GetType() == SMDSAbs_Volume )
3466     {
3467       SMDS_VolumeTool vol( elem );
3468       while ( nodeIt->more() ) {
3469         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470         if ( theNode != n && vol.IsLinked( theNode, n ))
3471           linkedNodes.insert( n );
3472       }
3473     }
3474     else
3475     {
3476       for ( int i = 0; nodeIt->more(); ++i ) {
3477         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478         if ( n == theNode ) {
3479           int iBefore = i - 1;
3480           int iAfter  = i + 1;
3481           if ( elem->IsQuadratic() ) {
3482             int nb = elem->NbNodes() / 2;
3483             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3485           }
3486           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3488         }
3489       }
3490     }
3491   }
3492 }
3493
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose  : pulls theNode toward the center of surrounding nodes directly
3497 //           connected to that node along an element edge
3498 //=======================================================================
3499
3500 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3501                      const Handle(Geom_Surface)&          theSurface,
3502                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3503 {
3504   // find surrounding nodes
3505
3506   TIDSortedElemSet nodeSet;
3507   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3508
3509   // compute new coodrs
3510
3511   double coord[] = { 0., 0., 0. };
3512   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515     if ( theSurface.IsNull() ) { // smooth in 3D
3516       coord[0] += node->X();
3517       coord[1] += node->Y();
3518       coord[2] += node->Z();
3519     }
3520     else { // smooth in 2D
3521       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522       gp_XY* uv = theUVMap[ node ];
3523       coord[0] += uv->X();
3524       coord[1] += uv->Y();
3525     }
3526   }
3527   int nbNodes = nodeSet.size();
3528   if ( !nbNodes )
3529     return;
3530   coord[0] /= nbNodes;
3531   coord[1] /= nbNodes;
3532
3533   if ( !theSurface.IsNull() ) {
3534     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3537     coord[0] = p3d.X();
3538     coord[1] = p3d.Y();
3539     coord[2] = p3d.Z();
3540   }
3541   else
3542     coord[2] /= nbNodes;
3543
3544   // move node
3545
3546   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 }
3548
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3552 //           surrounding elements
3553 //=======================================================================
3554
3555 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3556                       const Handle(Geom_Surface)&          theSurface,
3557                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3558 {
3559   gp_XYZ aNewXYZ(0.,0.,0.);
3560   SMESH::Controls::Area anAreaFunc;
3561   double totalArea = 0.;
3562   int nbElems = 0;
3563
3564   // compute new XYZ
3565
3566   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567   while ( elemIt->more() )
3568   {
3569     const SMDS_MeshElement* elem = elemIt->next();
3570     nbElems++;
3571
3572     gp_XYZ elemCenter(0.,0.,0.);
3573     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575     int nn = elem->NbNodes();
3576     if(elem->IsQuadratic()) nn = nn/2;
3577     int i=0;
3578     //while ( itN->more() ) {
3579     while ( i<nn ) {
3580       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3581       i++;
3582       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583       aNodePoints.push_back( aP );
3584       if ( !theSurface.IsNull() ) { // smooth in 2D
3585         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586         gp_XY* uv = theUVMap[ aNode ];
3587         aP.SetCoord( uv->X(), uv->Y(), 0. );
3588       }
3589       elemCenter += aP;
3590     }
3591     double elemArea = anAreaFunc.GetValue( aNodePoints );
3592     totalArea += elemArea;
3593     elemCenter /= nn;
3594     aNewXYZ += elemCenter * elemArea;
3595   }
3596   aNewXYZ /= totalArea;
3597   if ( !theSurface.IsNull() ) {
3598     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3600   }
3601
3602   // move node
3603
3604   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 }
3606
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose  : return UV of closest projection
3610 //=======================================================================
3611
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613                           const gp_Pnt&     point,
3614                           gp_XY &           result)
3615 {
3616   projector.Perform( point );
3617   if ( projector.IsDone() ) {
3618     double u, v, minVal = DBL_MAX;
3619     for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621       if ( projector.SquareDistance( i ) < minVal ) {
3622         minVal = projector.SquareDistance( i );
3623 #else
3624       if ( projector.Value( i ) < minVal ) {
3625         minVal = projector.Value( i );
3626 #endif
3627         projector.Point( i ).Parameter( u, v );
3628       }
3629     result.SetCoord( u, v );
3630     return true;
3631   }
3632   return false;
3633 }
3634
3635 //=======================================================================
3636 //function : Smooth
3637 //purpose  : Smooth theElements during theNbIterations or until a worst
3638 //           element has aspect ratio <= theTgtAspectRatio.
3639 //           Aspect Ratio varies in range [1.0, inf].
3640 //           If theElements is empty, the whole mesh is smoothed.
3641 //           theFixedNodes contains additionally fixed nodes. Nodes built
3642 //           on edges and boundary nodes are always fixed.
3643 //=======================================================================
3644
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3646                                set<const SMDS_MeshNode*> & theFixedNodes,
3647                                const SmoothMethod          theSmoothMethod,
3648                                const int                   theNbIterations,
3649                                double                      theTgtAspectRatio,
3650                                const bool                  the2D)
3651 {
3652   myLastCreatedElems.Clear();
3653   myLastCreatedNodes.Clear();
3654
3655   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3656
3657   if ( theTgtAspectRatio < 1.0 )
3658     theTgtAspectRatio = 1.0;
3659
3660   const double disttol = 1.e-16;
3661
3662   SMESH::Controls::AspectRatio aQualityFunc;
3663
3664   SMESHDS_Mesh* aMesh = GetMeshDS();
3665
3666   if ( theElems.empty() ) {
3667     // add all faces to theElems
3668     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669     while ( fIt->more() ) {
3670       const SMDS_MeshElement* face = fIt->next();
3671       theElems.insert( theElems.end(), face );
3672     }
3673   }
3674   // get all face ids theElems are on
3675   set< int > faceIdSet;
3676   TIDSortedElemSet::iterator itElem;
3677   if ( the2D )
3678     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679       int fId = FindShape( *itElem );
3680       // check that corresponding submesh exists and a shape is face
3681       if (fId &&
3682           faceIdSet.find( fId ) == faceIdSet.end() &&
3683           aMesh->MeshElements( fId )) {
3684         TopoDS_Shape F = aMesh->IndexToShape( fId );
3685         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686           faceIdSet.insert( fId );
3687       }
3688     }
3689   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3690
3691   // ===============================================
3692   // smooth elements on each TopoDS_Face separately
3693   // ===============================================
3694
3695   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696   for ( ; fId != faceIdSet.rend(); ++fId ) {
3697     // get face surface and submesh
3698     Handle(Geom_Surface) surface;
3699     SMESHDS_SubMesh* faceSubMesh = 0;
3700     TopoDS_Face face;
3701     double fToler2 = 0, f,l;
3702     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703     bool isUPeriodic = false, isVPeriodic = false;
3704     if ( *fId ) {
3705       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706       surface = BRep_Tool::Surface( face );
3707       faceSubMesh = aMesh->MeshElements( *fId );
3708       fToler2 = BRep_Tool::Tolerance( face );
3709       fToler2 *= fToler2 * 10.;
3710       isUPeriodic = surface->IsUPeriodic();
3711       if ( isUPeriodic )
3712         surface->UPeriod();
3713       isVPeriodic = surface->IsVPeriodic();
3714       if ( isVPeriodic )
3715         surface->VPeriod();
3716       surface->Bounds( u1, u2, v1, v2 );
3717     }
3718     // ---------------------------------------------------------
3719     // for elements on a face, find movable and fixed nodes and
3720     // compute UV for them
3721     // ---------------------------------------------------------
3722     bool checkBoundaryNodes = false;
3723     bool isQuadratic = false;
3724     set<const SMDS_MeshNode*> setMovableNodes;
3725     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727     list< const SMDS_MeshElement* > elemsOnFace;
3728
3729     Extrema_GenExtPS projector;
3730     GeomAdaptor_Surface surfAdaptor;
3731     if ( !surface.IsNull() ) {
3732       surfAdaptor.Load( surface );
3733       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3734     }
3735     int nbElemOnFace = 0;
3736     itElem = theElems.begin();
3737     // loop on not yet smoothed elements: look for elems on a face
3738     while ( itElem != theElems.end() ) {
3739       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740         break; // all elements found
3741
3742       const SMDS_MeshElement* elem = *itElem;
3743       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3745         ++itElem;
3746         continue;
3747       }
3748       elemsOnFace.push_back( elem );
3749       theElems.erase( itElem++ );
3750       nbElemOnFace++;
3751
3752       if ( !isQuadratic )
3753         isQuadratic = elem->IsQuadratic();
3754
3755       // get movable nodes of elem
3756       const SMDS_MeshNode* node;
3757       SMDS_TypeOfPosition posType;
3758       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759       int nn = 0, nbn =  elem->NbNodes();
3760       if(elem->IsQuadratic())
3761         nbn = nbn/2;
3762       while ( nn++ < nbn ) {
3763         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764         const SMDS_PositionPtr& pos = node->GetPosition();
3765         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766         if (posType != SMDS_TOP_EDGE &&
3767             posType != SMDS_TOP_VERTEX &&
3768             theFixedNodes.find( node ) == theFixedNodes.end())
3769         {
3770           // check if all faces around the node are on faceSubMesh
3771           // because a node on edge may be bound to face
3772           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773           bool all = true;
3774           if ( faceSubMesh ) {
3775             while ( eIt->more() && all ) {
3776               const SMDS_MeshElement* e = eIt->next();
3777               all = faceSubMesh->Contains( e );
3778             }
3779           }
3780           if ( all )
3781             setMovableNodes.insert( node );
3782           else
3783             checkBoundaryNodes = true;
3784         }
3785         if ( posType == SMDS_TOP_3DSPACE )
3786           checkBoundaryNodes = true;
3787       }
3788
3789       if ( surface.IsNull() )
3790         continue;
3791
3792       // get nodes to check UV
3793       list< const SMDS_MeshNode* > uvCheckNodes;
3794       itN = elem->nodesIterator();
3795       nn = 0; nbn =  elem->NbNodes();
3796       if(elem->IsQuadratic())
3797         nbn = nbn/2;
3798       while ( nn++ < nbn ) {
3799         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800         if ( uvMap.find( node ) == uvMap.end() )
3801           uvCheckNodes.push_back( node );
3802         // add nodes of elems sharing node
3803         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804         //         while ( eIt->more() ) {
3805         //           const SMDS_MeshElement* e = eIt->next();
3806         //           if ( e != elem ) {
3807         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808         //             while ( nIt->more() ) {
3809         //               const SMDS_MeshNode* n =
3810         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3811         //               if ( uvMap.find( n ) == uvMap.end() )
3812         //                 uvCheckNodes.push_back( n );
3813         //             }
3814         //           }
3815         //         }
3816       }
3817       // check UV on face
3818       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819       for ( ; n != uvCheckNodes.end(); ++n ) {
3820         node = *n;
3821         gp_XY uv( 0, 0 );
3822         const SMDS_PositionPtr& pos = node->GetPosition();
3823         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3824         // get existing UV
3825         switch ( posType ) {
3826         case SMDS_TOP_FACE: {
3827           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3829           break;
3830         }
3831         case SMDS_TOP_EDGE: {
3832           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833           Handle(Geom2d_Curve) pcurve;
3834           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836           if ( !pcurve.IsNull() ) {
3837             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838             uv = pcurve->Value( u ).XY();
3839           }
3840           break;
3841         }
3842         case SMDS_TOP_VERTEX: {
3843           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3846           break;
3847         }
3848         default:;
3849         }
3850         // check existing UV
3851         bool project = true;
3852         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853         double dist1 = DBL_MAX, dist2 = 0;
3854         if ( posType != SMDS_TOP_3DSPACE ) {
3855           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856           project = dist1 > fToler2;
3857         }
3858         if ( project ) { // compute new UV
3859           gp_XY newUV;
3860           if ( !getClosestUV( projector, pNode, newUV )) {
3861             MESSAGE("Node Projection Failed " << node);
3862           }
3863           else {
3864             if ( isUPeriodic )
3865               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3866             if ( isVPeriodic )
3867               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3868             // check new UV
3869             if ( posType != SMDS_TOP_3DSPACE )
3870               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871             if ( dist2 < dist1 )
3872               uv = newUV;
3873           }
3874         }
3875         // store UV in the map
3876         listUV.push_back( uv );
3877         uvMap.insert( make_pair( node, &listUV.back() ));
3878       }
3879     } // loop on not yet smoothed elements
3880
3881     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882       checkBoundaryNodes = true;
3883
3884     // fix nodes on mesh boundary
3885
3886     if ( checkBoundaryNodes ) {
3887       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888       map< SMESH_TLink, int >::iterator link_nb;
3889       // put all elements links to linkNbMap
3890       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892         const SMDS_MeshElement* elem = (*elemIt);
3893         int nbn =  elem->NbCornerNodes();
3894         // loop on elem links: insert them in linkNbMap
3895         for ( int iN = 0; iN < nbn; ++iN ) {
3896           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898           SMESH_TLink link( n1, n2 );
3899           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3900           link_nb->second++;
3901         }
3902       }
3903       // remove nodes that are in links encountered only once from setMovableNodes
3904       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905         if ( link_nb->second == 1 ) {
3906           setMovableNodes.erase( link_nb->first.node1() );
3907           setMovableNodes.erase( link_nb->first.node2() );
3908         }
3909       }
3910     }
3911
3912     // -----------------------------------------------------
3913     // for nodes on seam edge, compute one more UV ( uvMap2 );
3914     // find movable nodes linked to nodes on seam and which
3915     // are to be smoothed using the second UV ( uvMap2 )
3916     // -----------------------------------------------------
3917
3918     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919     if ( !surface.IsNull() ) {
3920       TopExp_Explorer eExp( face, TopAbs_EDGE );
3921       for ( ; eExp.More(); eExp.Next() ) {
3922         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923         if ( !BRep_Tool::IsClosed( edge, face ))
3924           continue;
3925         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926         if ( !sm ) continue;
3927         // find out which parameter varies for a node on seam
3928         double f,l;
3929         gp_Pnt2d uv1, uv2;
3930         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931         if ( pcurve.IsNull() ) continue;
3932         uv1 = pcurve->Value( f );
3933         edge.Reverse();
3934         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935         if ( pcurve.IsNull() ) continue;
3936         uv2 = pcurve->Value( f );
3937         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3938         // assure uv1 < uv2
3939         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3941         }
3942         // get nodes on seam and its vertices
3943         list< const SMDS_MeshNode* > seamNodes;
3944         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945         while ( nSeamIt->more() ) {
3946           const SMDS_MeshNode* node = nSeamIt->next();
3947           if ( !isQuadratic || !IsMedium( node ))
3948             seamNodes.push_back( node );
3949         }
3950         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951         for ( ; vExp.More(); vExp.Next() ) {
3952           sm = aMesh->MeshElements( vExp.Current() );
3953           if ( sm ) {
3954             nSeamIt = sm->GetNodes();
3955             while ( nSeamIt->more() )
3956               seamNodes.push_back( nSeamIt->next() );
3957           }
3958         }
3959         // loop on nodes on seam
3960         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962           const SMDS_MeshNode* nSeam = *noSeIt;
3963           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964           if ( n_uv == uvMap.end() )
3965             continue;
3966           // set the first UV
3967           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968           // set the second UV
3969           listUV.push_back( *n_uv->second );
3970           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971           if ( uvMap2.empty() )
3972             uvMap2 = uvMap; // copy the uvMap contents
3973           uvMap2[ nSeam ] = &listUV.back();
3974
3975           // collect movable nodes linked to ones on seam in nodesNearSeam
3976           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977           while ( eIt->more() ) {
3978             const SMDS_MeshElement* e = eIt->next();
3979             int nbUseMap1 = 0, nbUseMap2 = 0;
3980             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981             int nn = 0, nbn =  e->NbNodes();
3982             if(e->IsQuadratic()) nbn = nbn/2;
3983             while ( nn++ < nbn )
3984             {
3985               const SMDS_MeshNode* n =
3986                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3987               if (n == nSeam ||
3988                   setMovableNodes.find( n ) == setMovableNodes.end() )
3989                 continue;
3990               // add only nodes being closer to uv2 than to uv1
3991               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992                            0.5 * ( n->Y() + nSeam->Y() ),
3993                            0.5 * ( n->Z() + nSeam->Z() ));
3994               gp_XY uv;
3995               getClosestUV( projector, pMid, uv );
3996               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997                 nodesNearSeam.insert( n );
3998                 nbUseMap2++;
3999               }
4000               else
4001                 nbUseMap1++;
4002             }
4003             // for centroidalSmooth all element nodes must
4004             // be on one side of a seam
4005             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4007               nn = 0;
4008               while ( nn++ < nbn ) {
4009                 const SMDS_MeshNode* n =
4010                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4011                 setMovableNodes.erase( n );
4012               }
4013             }
4014           }
4015         } // loop on nodes on seam
4016       } // loop on edge of a face
4017     } // if ( !face.IsNull() )
4018
4019     if ( setMovableNodes.empty() ) {
4020       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021       continue; // goto next face
4022     }
4023
4024     // -------------
4025     // SMOOTHING //
4026     // -------------
4027
4028     int it = -1;
4029     double maxRatio = -1., maxDisplacement = -1.;
4030     set<const SMDS_MeshNode*>::iterator nodeToMove;
4031     for ( it = 0; it < theNbIterations; it++ ) {
4032       maxDisplacement = 0.;
4033       nodeToMove = setMovableNodes.begin();
4034       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035         const SMDS_MeshNode* node = (*nodeToMove);
4036         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037
4038         // smooth
4039         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040         if ( theSmoothMethod == LAPLACIAN )
4041           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4042         else
4043           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4044
4045         // node displacement
4046         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048         if ( aDispl > maxDisplacement )
4049           maxDisplacement = aDispl;
4050       }
4051       // no node movement => exit
4052       //if ( maxDisplacement < 1.e-16 ) {
4053       if ( maxDisplacement < disttol ) {
4054         MESSAGE("-- no node movement --");
4055         break;
4056       }
4057
4058       // check elements quality
4059       maxRatio  = 0;
4060       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062         const SMDS_MeshElement* elem = (*elemIt);
4063         if ( !elem || elem->GetType() != SMDSAbs_Face )
4064           continue;
4065         SMESH::Controls::TSequenceOfXYZ aPoints;
4066         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067           double aValue = aQualityFunc.GetValue( aPoints );
4068           if ( aValue > maxRatio )
4069             maxRatio = aValue;
4070         }
4071       }
4072       if ( maxRatio <= theTgtAspectRatio ) {
4073         MESSAGE("-- quality achived --");
4074         break;
4075       }
4076       if (it+1 == theNbIterations) {
4077         MESSAGE("-- Iteration limit exceeded --");
4078       }
4079     } // smoothing iterations
4080
4081     MESSAGE(" Face id: " << *fId <<
4082             " Nb iterstions: " << it <<
4083             " Displacement: " << maxDisplacement <<
4084             " Aspect Ratio " << maxRatio);
4085
4086     // ---------------------------------------
4087     // new nodes positions are computed,
4088     // record movement in DS and set new UV
4089     // ---------------------------------------
4090     nodeToMove = setMovableNodes.begin();
4091     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095       if ( node_uv != uvMap.end() ) {
4096         gp_XY* uv = node_uv->second;
4097         node->SetPosition
4098           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4099       }
4100     }
4101
4102     // move medium nodes of quadratic elements
4103     if ( isQuadratic )
4104     {
4105       SMESH_MesherHelper helper( *GetMesh() );
4106       helper.SetSubShape( face );
4107       vector<const SMDS_MeshNode*> nodes;
4108       bool checkUV;
4109       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4111       {
4112         const SMDS_MeshElement* QF = *elemIt;
4113         if ( QF->IsQuadratic() )
4114         {
4115           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116                         SMDS_MeshElement::iterator() );
4117           nodes.push_back( nodes[0] );
4118           gp_Pnt xyz;
4119           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4120           {
4121             if ( !surface.IsNull() )
4122             {
4123               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4126               xyz = surface->Value( uv.X(), uv.Y() );
4127             }
4128             else {
4129               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4130             }
4131             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132               // we have to move a medium node
4133               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4134           }
4135         }
4136       }
4137     }
4138
4139   } // loop on face ids
4140
4141 }
4142
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose  : Return true if normal of prevNodes is not co-directied with
4146 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 //           iNotSame is where prevNodes and nextNodes are different.
4148 //           If result is true then future volume orientation is OK
4149 //=======================================================================
4150
4151 static bool isReverse(const SMDS_MeshElement*             face,
4152                       const vector<const SMDS_MeshNode*>& prevNodes,
4153                       const vector<const SMDS_MeshNode*>& nextNodes,
4154                       const int                           iNotSame)
4155 {
4156
4157   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159   gp_XYZ extrDir( pN - pP ), faceNorm;
4160   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4161
4162   return faceNorm * extrDir < 0.0;
4163 }
4164
4165 //=======================================================================
4166 /*!
4167  * \brief Create elements by sweeping an element
4168  * \param elem - element to sweep
4169  * \param newNodesItVec - nodes generated from each node of the element
4170  * \param newElems - generated elements
4171  * \param nbSteps - number of sweeping steps
4172  * \param srcElements - to append elem for each generated element
4173  */
4174 //=======================================================================
4175
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4177                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178                                     list<const SMDS_MeshElement*>&        newElems,
4179                                     const int                             nbSteps,
4180                                     SMESH_SequenceOfElemPtr&              srcElements)
4181 {
4182   //MESSAGE("sweepElement " << nbSteps);
4183   SMESHDS_Mesh* aMesh = GetMeshDS();
4184
4185   const int           nbNodes = elem->NbNodes();
4186   const int         nbCorners = elem->NbCornerNodes();
4187   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188                                                           polyhedron creation !!! */
4189   // Loop on elem nodes:
4190   // find new nodes and detect same nodes indices
4191   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4195
4196   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197   vector<int> sames(nbNodes);
4198   vector<bool> isSingleNode(nbNodes);
4199
4200   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4202     const SMDS_MeshNode*                         node = nnIt->first;
4203     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204     if ( listNewNodes.empty() )
4205       return;
4206
4207     itNN   [ iNode ] = listNewNodes.begin();
4208     prevNod[ iNode ] = node;
4209     nextNod[ iNode ] = listNewNodes.front();
4210
4211     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212                                                              corner node of linear */
4213     if ( prevNod[ iNode ] != nextNod [ iNode ])
4214       nbDouble += !isSingleNode[iNode];
4215
4216     if( iNode < nbCorners ) { // check corners only
4217       if ( prevNod[ iNode ] == nextNod [ iNode ])
4218         sames[nbSame++] = iNode;
4219       else
4220         iNotSameNode = iNode;
4221     }
4222   }
4223
4224   if ( nbSame == nbNodes || nbSame > 2) {
4225     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4226     return;
4227   }
4228
4229   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4230   {
4231     // fix nodes order to have bottom normal external
4232     if ( baseType == SMDSEntity_Polygon )
4233     {
4234       std::reverse( itNN.begin(), itNN.end() );
4235       std::reverse( prevNod.begin(), prevNod.end() );
4236       std::reverse( midlNod.begin(), midlNod.end() );
4237       std::reverse( nextNod.begin(), nextNod.end() );
4238       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4239     }
4240     else
4241     {
4242       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243       SMDS_MeshCell::applyInterlace( ind, itNN );
4244       SMDS_MeshCell::applyInterlace( ind, prevNod );
4245       SMDS_MeshCell::applyInterlace( ind, nextNod );
4246       SMDS_MeshCell::applyInterlace( ind, midlNod );
4247       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4248       if ( nbSame > 0 )
4249       {
4250         sames[nbSame] = iNotSameNode;
4251         for ( int j = 0; j <= nbSame; ++j )
4252           for ( size_t i = 0; i < ind.size(); ++i )
4253             if ( ind[i] == sames[j] )
4254             {
4255               sames[j] = i;
4256               break;
4257             }
4258         iNotSameNode = sames[nbSame];
4259       }
4260     }
4261   }
4262
4263   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4264   if ( nbSame > 0 ) {
4265     iSameNode    = sames[ nbSame-1 ];
4266     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4268     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4269   }
4270
4271   // make new elements
4272   for (int iStep = 0; iStep < nbSteps; iStep++ )
4273   {
4274     // get next nodes
4275     for ( iNode = 0; iNode < nbNodes; iNode++ )
4276     {
4277       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278       nextNod[ iNode ] = *itNN[ iNode ]++;
4279     }
4280
4281     SMDS_MeshElement* aNewElem = 0;
4282     /*if(!elem->IsPoly())*/ {
4283       switch ( baseType ) {
4284       case SMDSEntity_0D:
4285       case SMDSEntity_Node: { // sweep NODE
4286         if ( nbSame == 0 ) {
4287           if ( isSingleNode[0] )
4288             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4289           else
4290             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4291         }
4292         else
4293           return;
4294         break;
4295       }
4296       case SMDSEntity_Edge: { // sweep EDGE
4297         if ( nbDouble == 0 )
4298         {
4299           if ( nbSame == 0 ) // ---> quadrangle
4300             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301                                       nextNod[ 1 ], nextNod[ 0 ] );
4302           else               // ---> triangle
4303             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304                                       nextNod[ iNotSameNode ] );
4305         }
4306         else                 // ---> polygon
4307         {
4308           vector<const SMDS_MeshNode*> poly_nodes;
4309           poly_nodes.push_back( prevNod[0] );
4310           poly_nodes.push_back( prevNod[1] );
4311           if ( prevNod[1] != nextNod[1] )
4312           {
4313             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314             poly_nodes.push_back( nextNod[1] );
4315           }
4316           if ( prevNod[0] != nextNod[0] )
4317           {
4318             poly_nodes.push_back( nextNod[0] );
4319             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4320           }
4321           switch ( poly_nodes.size() ) {
4322           case 3:
4323             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4324             break;
4325           case 4:
4326             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4328             break;
4329           default:
4330             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4331           }
4332         }
4333         break;
4334       }
4335       case SMDSEntity_Triangle: // TRIANGLE --->
4336         {
4337           if ( nbDouble > 0 ) break;
4338           if ( nbSame == 0 )       // ---> pentahedron
4339             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4341
4342           else if ( nbSame == 1 )  // ---> pyramid
4343             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4345                                          nextNod[ iSameNode ]);
4346
4347           else // 2 same nodes:       ---> tetrahedron
4348             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349                                          nextNod[ iNotSameNode ]);
4350           break;
4351         }
4352       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4353         {
4354           if ( nbSame == 2 )
4355             return;
4356           if ( nbDouble+nbSame == 2 )
4357           {
4358             if(nbSame==0) {      // ---> quadratic quadrangle
4359               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4361             }
4362             else { //(nbSame==1) // ---> quadratic triangle
4363               if(sames[0]==2) {
4364                 return; // medium node on axis
4365               }
4366               else if(sames[0]==0)
4367                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368                                           nextNod[2], midlNod[1], prevNod[2]);
4369               else // sames[0]==1
4370                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371                                           midlNod[0], nextNod[2], prevNod[2]);
4372             }
4373           }
4374           else if ( nbDouble == 3 )
4375           {
4376             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4377               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4379             }
4380           }
4381           else
4382             return;
4383           break;
4384         }
4385       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386         if ( nbDouble > 0 ) break;
4387
4388         if ( nbSame == 0 )       // ---> hexahedron
4389           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4391
4392         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4395                                        nextNod[ iSameNode ]);
4396           newElems.push_back( aNewElem );
4397           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4398                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4400         }
4401         else if ( nbSame == 2 ) { // ---> pentahedron
4402           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403             // iBeforeSame is same too
4404             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4406                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4407           else
4408             // iAfterSame is same too
4409             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4410                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4412         }
4413         break;
4414       }
4415       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4416       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4417         if ( nbDouble+nbSame != 3 ) break;
4418         if(nbSame==0) {
4419           // --->  pentahedron with 15 nodes
4420           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421                                        nextNod[0], nextNod[1], nextNod[2],
4422                                        prevNod[3], prevNod[4], prevNod[5],
4423                                        nextNod[3], nextNod[4], nextNod[5],
4424                                        midlNod[0], midlNod[1], midlNod[2]);
4425         }
4426         else if(nbSame==1) {
4427           // --->  2d order pyramid of 13 nodes
4428           int apex = iSameNode;
4429           int i0 = ( apex + 1 ) % nbCorners;
4430           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4431           int i0a = apex + 3;
4432           int i1a = i1 + 3;
4433           int i01 = i0 + 3;
4434           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435                                       nextNod[i0], nextNod[i1], prevNod[apex],
4436                                       prevNod[i01], midlNod[i0],
4437                                       nextNod[i01], midlNod[i1],
4438                                       prevNod[i1a], prevNod[i0a],
4439                                       nextNod[i0a], nextNod[i1a]);
4440         }
4441         else if(nbSame==2) {
4442           // --->  2d order tetrahedron of 10 nodes
4443           int n1 = iNotSameNode;
4444           int n2 = ( n1 + 1             ) % nbCorners;
4445           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4446           int n12 = n1 + 3;
4447           int n23 = n2 + 3;
4448           int n31 = n3 + 3;
4449           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450                                        prevNod[n12], prevNod[n23], prevNod[n31],
4451                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4452         }
4453         break;
4454       }
4455       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4456         if( nbSame == 0 ) {
4457           if ( nbDouble != 4 ) break;
4458           // --->  hexahedron with 20 nodes
4459           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4464         }
4465         else if(nbSame==1) {
4466           // ---> pyramid + pentahedron - can not be created since it is needed
4467           // additional middle node at the center of face
4468           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4469           return;
4470         }
4471         else if( nbSame == 2 ) {
4472           if ( nbDouble != 2 ) break;
4473           // --->  2d order Pentahedron with 15 nodes
4474           int n1,n2,n4,n5;
4475           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476             // iBeforeSame is same too
4477             n1 = iBeforeSame;
4478             n2 = iOpposSame;
4479             n4 = iSameNode;
4480             n5 = iAfterSame;
4481           }
4482           else {
4483             // iAfterSame is same too
4484             n1 = iSameNode;
4485             n2 = iBeforeSame;
4486             n4 = iAfterSame;
4487             n5 = iOpposSame;
4488           }
4489           int n12 = n2 + 4;
4490           int n45 = n4 + 4;
4491           int n14 = n1 + 4;
4492           int n25 = n5 + 4;
4493           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494                                        prevNod[n4], prevNod[n5], nextNod[n5],
4495                                        prevNod[n12], midlNod[n2], nextNod[n12],
4496                                        prevNod[n45], midlNod[n5], nextNod[n45],
4497                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4498         }
4499         break;
4500       }
4501       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4502
4503         if( nbSame == 0 && nbDouble == 9 ) {
4504           // --->  tri-quadratic hexahedron with 27 nodes
4505           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510                                        prevNod[8], // bottom center
4511                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512                                        nextNod[8], // top center
4513                                        midlNod[8]);// elem center
4514         }
4515         else
4516         {
4517           return;
4518         }
4519         break;
4520       }
4521       case SMDSEntity_Polygon: { // sweep POLYGON
4522
4523         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524           // --->  hexagonal prism
4525           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526                                        prevNod[3], prevNod[4], prevNod[5],
4527                                        nextNod[0], nextNod[1], nextNod[2],
4528                                        nextNod[3], nextNod[4], nextNod[5]);
4529         }
4530         break;
4531       }
4532       case SMDSEntity_Ball:
4533         return;
4534
4535       default:
4536         break;
4537       }
4538     }
4539
4540     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4541     {
4542       if ( baseType != SMDSEntity_Polygon )
4543       {
4544         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545         SMDS_MeshCell::applyInterlace( ind, prevNod );
4546         SMDS_MeshCell::applyInterlace( ind, nextNod );
4547         SMDS_MeshCell::applyInterlace( ind, midlNod );
4548         SMDS_MeshCell::applyInterlace( ind, itNN );
4549         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4551       }
4552       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553       vector<int> quantities (nbNodes + 2);
4554       polyedre_nodes.clear();
4555       quantities.clear();
4556
4557       // bottom of prism
4558       for (int inode = 0; inode < nbNodes; inode++)
4559         polyedre_nodes.push_back( prevNod[inode] );
4560       quantities.push_back( nbNodes );
4561
4562       // top of prism
4563       polyedre_nodes.push_back( nextNod[0] );
4564       for (int inode = nbNodes; inode-1; --inode )
4565         polyedre_nodes.push_back( nextNod[inode-1] );
4566       quantities.push_back( nbNodes );
4567
4568       // side faces
4569       for (int iface = 0; iface < nbNodes; iface++)
4570       {
4571         const int prevNbNodes = polyedre_nodes.size();
4572         int inextface = (iface+1) % nbNodes;
4573         polyedre_nodes.push_back( prevNod[inextface] );
4574         polyedre_nodes.push_back( prevNod[iface] );
4575         if ( prevNod[iface] != nextNod[iface] )
4576         {
4577           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578           polyedre_nodes.push_back( nextNod[iface] );
4579         }
4580         if ( prevNod[inextface] != nextNod[inextface] )
4581         {
4582           polyedre_nodes.push_back( nextNod[inextface] );
4583           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4584         }
4585         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586         if ( nbFaceNodes > 2 )
4587           quantities.push_back( nbFaceNodes );
4588         else // degenerated face
4589           polyedre_nodes.resize( prevNbNodes );
4590       }
4591       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4592     }
4593
4594     if ( aNewElem ) {
4595       newElems.push_back( aNewElem );
4596       myLastCreatedElems.Append(aNewElem);
4597       srcElements.Append( elem );
4598     }
4599
4600     // set new prev nodes
4601     for ( iNode = 0; iNode < nbNodes; iNode++ )
4602       prevNod[ iNode ] = nextNod[ iNode ];
4603
4604   } // for steps
4605 }
4606
4607 //=======================================================================
4608 /*!
4609  * \brief Create 1D and 2D elements around swept elements
4610  * \param mapNewNodes - source nodes and ones generated from them
4611  * \param newElemsMap - source elements and ones generated from them
4612  * \param elemNewNodesMap - nodes generated from each node of each element
4613  * \param elemSet - all swept elements
4614  * \param nbSteps - number of sweeping steps
4615  * \param srcElements - to append elem for each generated element
4616  */
4617 //=======================================================================
4618
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4620                                   TTElemOfElemListMap &    newElemsMap,
4621                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4622                                   TIDSortedElemSet&        elemSet,
4623                                   const int                nbSteps,
4624                                   SMESH_SequenceOfElemPtr& srcElements)
4625 {
4626   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627   SMESHDS_Mesh* aMesh = GetMeshDS();
4628
4629   // Find nodes belonging to only one initial element - sweep them into edges.
4630
4631   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632   for ( ; nList != mapNewNodes.end(); nList++ )
4633   {
4634     const SMDS_MeshNode* node =
4635       static_cast<const SMDS_MeshNode*>( nList->first );
4636     if ( newElemsMap.count( node ))
4637       continue; // node was extruded into edge
4638     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639     int nbInitElems = 0;
4640     const SMDS_MeshElement* el = 0;
4641     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642     while ( eIt->more() && nbInitElems < 2 ) {
4643       el = eIt->next();
4644       SMDSAbs_ElementType type = el->GetType();
4645       if ( type == SMDSAbs_Volume || type < highType ) continue;
4646       if ( type > highType ) {
4647         nbInitElems = 0;
4648         highType = type;
4649       }
4650       nbInitElems += elemSet.count(el);
4651     }
4652     if ( nbInitElems < 2 ) {
4653       bool NotCreateEdge = el && el->IsMediumNode(node);
4654       if(!NotCreateEdge) {
4655         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656         list<const SMDS_MeshElement*> newEdges;
4657         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4658       }
4659     }
4660   }
4661
4662   // Make a ceiling for each element ie an equal element of last new nodes.
4663   // Find free links of faces - make edges and sweep them into faces.
4664
4665   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4666   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4668   {
4669     const SMDS_MeshElement* elem = itElem->first;
4670     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4671
4672     if(itElem->second.size()==0) continue;
4673
4674     const bool isQuadratic = elem->IsQuadratic();
4675
4676     if ( elem->GetType() == SMDSAbs_Edge ) {
4677       // create a ceiling edge
4678       if ( !isQuadratic ) {
4679         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680                                vecNewNodes[ 1 ]->second.back())) {
4681           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682                                                    vecNewNodes[ 1 ]->second.back()));
4683           srcElements.Append( elem );
4684         }
4685       }
4686       else {
4687         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688                                vecNewNodes[ 1 ]->second.back(),
4689                                vecNewNodes[ 2 ]->second.back())) {
4690           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691                                                    vecNewNodes[ 1 ]->second.back(),
4692                                                    vecNewNodes[ 2 ]->second.back()));
4693           srcElements.Append( elem );
4694         }
4695       }
4696     }
4697     if ( elem->GetType() != SMDSAbs_Face )
4698       continue;
4699
4700     bool hasFreeLinks = false;
4701
4702     TIDSortedElemSet avoidSet;
4703     avoidSet.insert( elem );
4704
4705     set<const SMDS_MeshNode*> aFaceLastNodes;
4706     int iNode, nbNodes = vecNewNodes.size();
4707     if ( !isQuadratic ) {
4708       // loop on the face nodes
4709       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711         // look for free links of the face
4712         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715         // check if a link n1-n2 is free
4716         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717           hasFreeLinks = true;
4718           // make a new edge and a ceiling for a new edge
4719           const SMDS_MeshElement* edge;
4720           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722             srcElements.Append( myLastCreatedElems.Last() );
4723           }
4724           n1 = vecNewNodes[ iNode ]->second.back();
4725           n2 = vecNewNodes[ iNext ]->second.back();
4726           if ( !aMesh->FindEdge( n1, n2 )) {
4727             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728             srcElements.Append( edge );
4729           }
4730         }
4731       }
4732     }
4733     else { // elem is quadratic face
4734       int nbn = nbNodes/2;
4735       for ( iNode = 0; iNode < nbn; iNode++ ) {
4736         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741         // check if a link is free
4742         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745           hasFreeLinks = true;
4746           // make an edge and a ceiling for a new edge
4747           // find medium node
4748           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750             srcElements.Append( elem );
4751           }
4752           n1 = vecNewNodes[ iNode ]->second.back();
4753           n2 = vecNewNodes[ iNext ]->second.back();
4754           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757             srcElements.Append( elem );
4758           }
4759         }
4760       }
4761       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4763       }
4764     }
4765
4766     // sweep free links into faces
4767
4768     if ( hasFreeLinks )  {
4769       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4771
4772       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4777       }
4778       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4779         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4781       }
4782       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784         std::advance( v, volNb );
4785         // find indices of free faces of a volume and their source edges
4786         list< int > freeInd;
4787         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789         int iF, nbF = vTool.NbFaces();
4790         for ( iF = 0; iF < nbF; iF ++ ) {
4791           if (vTool.IsFreeFace( iF ) &&
4792               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793               initNodeSet != faceNodeSet) // except an initial face
4794           {
4795             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4796               continue;
4797             if ( faceNodeSet == initNodeSetNoCenter )
4798               continue;
4799             freeInd.push_back( iF );
4800             // find source edge of a free face iF
4801             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804                                    initNodeSet.begin(), initNodeSet.end(),
4805                                    commonNodes.begin());
4806             if ( (*v)->IsQuadratic() )
4807               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4808             else
4809               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4810 #ifdef _DEBUG_
4811             if ( !srcEdges.back() )
4812             {
4813               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814                    << iF << " of volume #" << vTool.ID() << endl;
4815             }
4816 #endif
4817           }
4818         }
4819         if ( freeInd.empty() )
4820           continue;
4821
4822         // create faces for all steps;
4823         // if such a face has been already created by sweep of edge,
4824         // assure that its orientation is OK
4825         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827           vTool.SetExternalNormal();
4828           const int nextShift = vTool.IsForward() ? +1 : -1;
4829           list< int >::iterator ind = freeInd.begin();
4830           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4832           {
4833             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834             int nbn = vTool.NbFaceNodes( *ind );
4835             const SMDS_MeshElement * f = 0;
4836             if ( nbn == 3 )              ///// triangle
4837             {
4838               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4839               if ( !f ||
4840                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4841               {
4842                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4843                                                      nodes[ 1 ],
4844                                                      nodes[ 1 + nextShift ] };
4845                 if ( f )
4846                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4847                 else
4848                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4849                                                             newOrder[ 2 ] ));
4850               }
4851             }
4852             else if ( nbn == 4 )       ///// quadrangle
4853             {
4854               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4855               if ( !f ||
4856                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4857               {
4858                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4860                 if ( f )
4861                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4862                 else
4863                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864                                                             newOrder[ 2 ], newOrder[ 3 ]));
4865               }
4866             }
4867             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4868             {
4869               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4870               if ( !f ||
4871                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4872               {
4873                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4874                                                      nodes[2],
4875                                                      nodes[2 + 2*nextShift],
4876                                                      nodes[3 - 2*nextShift],
4877                                                      nodes[3],
4878                                                      nodes[3 + 2*nextShift]};
4879                 if ( f )
4880                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4881                 else
4882                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4883                                                             newOrder[ 1 ],
4884                                                             newOrder[ 2 ],
4885                                                             newOrder[ 3 ],
4886                                                             newOrder[ 4 ],
4887                                                             newOrder[ 5 ] ));
4888               }
4889             }
4890             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4891             {
4892               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4894               if ( !f ||
4895                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4896               {
4897                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898                                                      nodes[4 - 2*nextShift],
4899                                                      nodes[4],
4900                                                      nodes[4 + 2*nextShift],
4901                                                      nodes[1],
4902                                                      nodes[5 - 2*nextShift],
4903                                                      nodes[5],
4904                                                      nodes[5 + 2*nextShift] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909                                                            newOrder[ 2 ], newOrder[ 3 ],
4910                                                            newOrder[ 4 ], newOrder[ 5 ],
4911                                                            newOrder[ 6 ], newOrder[ 7 ]));
4912               }
4913             }
4914             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4915             {
4916               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917                                       SMDSAbs_Face, /*noMedium=*/false);
4918               if ( !f ||
4919                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922                                                      nodes[4 - 2*nextShift],
4923                                                      nodes[4],
4924                                                      nodes[4 + 2*nextShift],
4925                                                      nodes[1],
4926                                                      nodes[5 - 2*nextShift],
4927                                                      nodes[5],
4928                                                      nodes[5 + 2*nextShift],
4929                                                      nodes[8] };
4930                 if ( f )
4931                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932                 else
4933                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934                                                            newOrder[ 2 ], newOrder[ 3 ],
4935                                                            newOrder[ 4 ], newOrder[ 5 ],
4936                                                            newOrder[ 6 ], newOrder[ 7 ],
4937                                                            newOrder[ 8 ]));
4938               }
4939             }
4940             else  //////// polygon
4941             {
4942               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4944               if ( !f ||
4945                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4946               {
4947                 if ( !vTool.IsForward() )
4948                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4949                 if ( f )
4950                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4951                 else
4952                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4953               }
4954             }
4955
4956             while ( srcElements.Length() < myLastCreatedElems.Length() )
4957               srcElements.Append( *srcEdge );
4958
4959           }  // loop on free faces
4960
4961           // go to the next volume
4962           iVol = 0;
4963           while ( iVol++ < nbVolumesByStep ) v++;
4964
4965         } // loop on steps
4966       } // loop on volumes of one step
4967     } // sweep free links into faces
4968
4969     // Make a ceiling face with a normal external to a volume
4970
4971     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4974
4975     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4978     }
4979     if ( iF >= 0 ) {
4980       lastVol.SetExternalNormal();
4981       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982       int nbn = lastVol.NbFaceNodes( iF );
4983       // we do not use this->AddElement() because nodes are interlaced
4984       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985       if ( !hasFreeLinks ||
4986            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4987       {
4988         if ( nbn == 3 )
4989           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4990
4991         else if ( nbn == 4 )
4992           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4993
4994         else if ( nbn == 6 && isQuadratic )
4995           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996                                                     nodes[1], nodes[3], nodes[5]));
4997         else if ( nbn == 7 && isQuadratic )
4998           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5000         else if ( nbn == 8 && isQuadratic )
5001           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5003         else if ( nbn == 9 && isQuadratic )
5004           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5006                                                     nodes[8]));
5007         else
5008           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5009
5010         while ( srcElements.Length() < myLastCreatedElems.Length() )
5011           srcElements.Append( elem );
5012       }
5013     }
5014   } // loop on swept elements
5015 }
5016
5017 //=======================================================================
5018 //function : RotationSweep
5019 //purpose  :
5020 //=======================================================================
5021
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024                                 const gp_Ax1&      theAxis,
5025                                 const double       theAngle,
5026                                 const int          theNbSteps,
5027                                 const double       theTol,
5028                                 const bool         theMakeGroups,
5029                                 const bool         theMakeWalls)
5030 {
5031   myLastCreatedElems.Clear();
5032   myLastCreatedNodes.Clear();
5033
5034   // source elements for each generated one
5035   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5036
5037   MESSAGE( "RotationSweep()");
5038   gp_Trsf aTrsf;
5039   aTrsf.SetRotation( theAxis, theAngle );
5040   gp_Trsf aTrsf2;
5041   aTrsf2.SetRotation( theAxis, theAngle/2. );
5042
5043   gp_Lin aLine( theAxis );
5044   double aSqTol = theTol * theTol;
5045
5046   SMESHDS_Mesh* aMesh = GetMeshDS();
5047
5048   TNodeOfNodeListMap mapNewNodes;
5049   TElemOfVecOfNnlmiMap mapElemNewNodes;
5050   TTElemOfElemListMap newElemsMap;
5051
5052   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5054                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5055   // loop on theElems
5056   TIDSortedElemSet::iterator itElem;
5057   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058     const SMDS_MeshElement* elem = *itElem;
5059     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5060       continue;
5061     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062     newNodesItVec.reserve( elem->NbNodes() );
5063
5064     // loop on elem nodes
5065     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066     while ( itN->more() )
5067     {
5068       // check if a node has been already sweeped
5069       const SMDS_MeshNode* node = cast2Node( itN->next() );
5070
5071       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5072       double coord[3];
5073       aXYZ.Coord( coord[0], coord[1], coord[2] );
5074       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5075
5076       TNodeOfNodeListMapItr nIt =
5077         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079       if ( listNewNodes.empty() )
5080       {
5081         // check if we are to create medium nodes between corner ones
5082         bool needMediumNodes = false;
5083         if ( isQuadraticMesh )
5084         {
5085           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086           while (it->more() && !needMediumNodes )
5087           {
5088             const SMDS_MeshElement* invElem = it->next();
5089             if ( invElem != elem && !theElems.count( invElem )) continue;
5090             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092               needMediumNodes = true;
5093           }
5094         }
5095
5096         // make new nodes
5097         const SMDS_MeshNode * newNode = node;
5098         for ( int i = 0; i < theNbSteps; i++ ) {
5099           if ( !isOnAxis ) {
5100             if ( needMediumNodes )  // create a medium node
5101             {
5102               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104               myLastCreatedNodes.Append(newNode);
5105               srcNodes.Append( node );
5106               listNewNodes.push_back( newNode );
5107               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5108             }
5109             else {
5110               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5111             }
5112             // create a corner node
5113             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114             myLastCreatedNodes.Append(newNode);
5115             srcNodes.Append( node );
5116             listNewNodes.push_back( newNode );
5117           }
5118           else {
5119             listNewNodes.push_back( newNode );
5120             // if ( needMediumNodes )
5121             //   listNewNodes.push_back( newNode );
5122           }
5123         }
5124       }
5125       newNodesItVec.push_back( nIt );
5126     }
5127     // make new elements
5128     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5129   }
5130
5131   if ( theMakeWalls )
5132     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5133
5134   PGroupIDs newGroupIDs;
5135   if ( theMakeGroups )
5136     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5137
5138   return newGroupIDs;
5139 }
5140
5141
5142 //=======================================================================
5143 //function : CreateNode
5144 //purpose  :
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5147                                                   const double y,
5148                                                   const double z,
5149                                                   const double tolnode,
5150                                                   SMESH_SequenceOfNode& aNodes)
5151 {
5152   // myLastCreatedElems.Clear();
5153   // myLastCreatedNodes.Clear();
5154
5155   gp_Pnt P1(x,y,z);
5156   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5157
5158   // try to search in sequence of existing nodes
5159   // if aNodes.Length()>0 we 'nave to use given sequence
5160   // else - use all nodes of mesh
5161   if(aNodes.Length()>0) {
5162     int i;
5163     for(i=1; i<=aNodes.Length(); i++) {
5164       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165       if(P1.Distance(P2)<tolnode)
5166         return aNodes.Value(i);
5167     }
5168   }
5169   else {
5170     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171     while(itn->more()) {
5172       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174       if(P1.Distance(P2)<tolnode)
5175         return aN;
5176     }
5177   }
5178
5179   // create new node and return it
5180   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181   //myLastCreatedNodes.Append(NewNode);
5182   return NewNode;
5183 }
5184
5185
5186 //=======================================================================
5187 //function : ExtrusionSweep
5188 //purpose  :
5189 //=======================================================================
5190
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5193                                   const gp_Vec&        theStep,
5194                                   const int            theNbSteps,
5195                                   TTElemOfElemListMap& newElemsMap,
5196                                   const bool           theMakeGroups,
5197                                   const int            theFlags,
5198                                   const double         theTolerance)
5199 {
5200   ExtrusParam aParams;
5201   aParams.myDir = gp_Dir(theStep);
5202   aParams.myNodes.Clear();
5203   aParams.mySteps = new TColStd_HSequenceOfReal;
5204   int i;
5205   for(i=1; i<=theNbSteps; i++)
5206     aParams.mySteps->Append(theStep.Magnitude());
5207
5208   return
5209     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5210 }
5211
5212
5213 //=======================================================================
5214 //function : ExtrusionSweep
5215 //purpose  :
5216 //=======================================================================
5217
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5220                                   ExtrusParam&         theParams,
5221                                   TTElemOfElemListMap& newElemsMap,
5222                                   const bool           theMakeGroups,
5223                                   const int            theFlags,
5224                                   const double         theTolerance)
5225 {
5226   myLastCreatedElems.Clear();
5227   myLastCreatedNodes.Clear();
5228
5229   // source elements for each generated one
5230   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5231
5232   SMESHDS_Mesh* aMesh = GetMeshDS();
5233
5234   int nbsteps = theParams.mySteps->Length();
5235
5236   TNodeOfNodeListMap mapNewNodes;
5237   //TNodeOfNodeVecMap mapNewNodes;
5238   TElemOfVecOfNnlmiMap mapElemNewNodes;
5239   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5240
5241   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5243                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5244   // loop on theElems
5245   TIDSortedElemSet::iterator itElem;
5246   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247     // check element type
5248     const SMDS_MeshElement* elem = *itElem;
5249     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5250       continue;
5251
5252     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253     newNodesItVec.reserve( elem->NbNodes() );
5254
5255     // loop on elem nodes
5256     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257     while ( itN->more() )
5258     {
5259       // check if a node has been already sweeped
5260       const SMDS_MeshNode* node = cast2Node( itN->next() );
5261       TNodeOfNodeListMap::iterator nIt =
5262         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264       if ( listNewNodes.empty() )
5265       {
5266         // make new nodes
5267
5268         // check if we are to create medium nodes between corner ones
5269         bool needMediumNodes = false;
5270         if ( isQuadraticMesh )
5271         {
5272           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273           while (it->more() && !needMediumNodes )
5274           {
5275             const SMDS_MeshElement* invElem = it->next();
5276             if ( invElem != elem && !theElems.count( invElem )) continue;
5277             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279               needMediumNodes = true;
5280           }
5281         }
5282
5283         double coord[] = { node->X(), node->Y(), node->Z() };
5284         for ( int i = 0; i < nbsteps; i++ )
5285         {
5286           if ( needMediumNodes ) // create a medium node
5287           {
5288             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291             if( theFlags & EXTRUSION_FLAG_SEW ) {
5292               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293                                                          theTolerance, theParams.myNodes);
5294               listNewNodes.push_back( newNode );
5295             }
5296             else {
5297               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298               myLastCreatedNodes.Append(newNode);
5299               srcNodes.Append( node );
5300               listNewNodes.push_back( newNode );
5301             }
5302           }
5303           // create a corner node
5304           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307           if( theFlags & EXTRUSION_FLAG_SEW ) {
5308             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309                                                        theTolerance, theParams.myNodes);
5310             listNewNodes.push_back( newNode );
5311           }
5312           else {
5313             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314             myLastCreatedNodes.Append(newNode);
5315             srcNodes.Append( node );
5316             listNewNodes.push_back( newNode );
5317           }
5318         }
5319       }
5320       newNodesItVec.push_back( nIt );
5321     }
5322     // make new elements
5323     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5324   }
5325
5326   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5328   }
5329   PGroupIDs newGroupIDs;
5330   if ( theMakeGroups )
5331     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5332
5333   return newGroupIDs;
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5338 //purpose  :
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5342                                        SMESH_subMesh*       theTrack,
5343                                        const SMDS_MeshNode* theN1,
5344                                        const bool           theHasAngles,
5345                                        list<double>&        theAngles,
5346                                        const bool           theLinearVariation,
5347                                        const bool           theHasRefPoint,
5348                                        const gp_Pnt&        theRefPoint,
5349                                        const bool           theMakeGroups)
5350 {
5351   MESSAGE("ExtrusionAlongTrack");
5352   myLastCreatedElems.Clear();
5353   myLastCreatedNodes.Clear();
5354
5355   int aNbE;
5356   std::list<double> aPrms;
5357   TIDSortedElemSet::iterator itElem;
5358
5359   gp_XYZ aGC;
5360   TopoDS_Edge aTrackEdge;
5361   TopoDS_Vertex aV1, aV2;
5362
5363   SMDS_ElemIteratorPtr aItE;
5364   SMDS_NodeIteratorPtr aItN;
5365   SMDSAbs_ElementType aTypeE;
5366
5367   TNodeOfNodeListMap mapNewNodes;
5368
5369   // 1. Check data
5370   aNbE = theElements.size();
5371   // nothing to do
5372   if ( !aNbE )
5373     return EXTR_NO_ELEMENTS;
5374
5375   // 1.1 Track Pattern
5376   ASSERT( theTrack );
5377
5378   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5379
5380   aItE = pSubMeshDS->GetElements();
5381   while ( aItE->more() ) {
5382     const SMDS_MeshElement* pE = aItE->next();
5383     aTypeE = pE->GetType();
5384     // Pattern must contain links only
5385     if ( aTypeE != SMDSAbs_Edge )
5386       return EXTR_PATH_NOT_EDGE;
5387   }
5388
5389   list<SMESH_MeshEditor_PathPoint> fullList;
5390
5391   const TopoDS_Shape& aS = theTrack->GetSubShape();
5392   // Sub-shape for the Pattern must be an Edge or Wire
5393   if( aS.ShapeType() == TopAbs_EDGE ) {
5394     aTrackEdge = TopoDS::Edge( aS );
5395     // the Edge must not be degenerated
5396     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397       return EXTR_BAD_PATH_SHAPE;
5398     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400     const SMDS_MeshNode* aN1 = aItN->next();
5401     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402     const SMDS_MeshNode* aN2 = aItN->next();
5403     // starting node must be aN1 or aN2
5404     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405       return EXTR_BAD_STARTING_NODE;
5406     aItN = pSubMeshDS->GetNodes();
5407     while ( aItN->more() ) {
5408       const SMDS_MeshNode* pNode = aItN->next();
5409       const SMDS_EdgePosition* pEPos =
5410         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411       double aT = pEPos->GetUParameter();
5412       aPrms.push_back( aT );
5413     }
5414     //Extrusion_Error err =
5415     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417     list< SMESH_subMesh* > LSM;
5418     TopTools_SequenceOfShape Edges;
5419     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420     while(itSM->more()) {
5421       SMESH_subMesh* SM = itSM->next();
5422       LSM.push_back(SM);
5423       const TopoDS_Shape& aS = SM->GetSubShape();
5424       Edges.Append(aS);
5425     }
5426     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427     int startNid = theN1->GetID();
5428     TColStd_MapOfInteger UsedNums;
5429
5430     int NbEdges = Edges.Length();
5431     int i = 1;
5432     for(; i<=NbEdges; i++) {
5433       int k = 0;
5434       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435       for(; itLSM!=LSM.end(); itLSM++) {
5436         k++;
5437         if(UsedNums.Contains(k)) continue;
5438         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439         SMESH_subMesh* locTrack = *itLSM;
5440         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443         const SMDS_MeshNode* aN1 = aItN->next();
5444         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445         const SMDS_MeshNode* aN2 = aItN->next();
5446         // starting node must be aN1 or aN2
5447         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448         // 2. Collect parameters on the track edge
5449         aPrms.clear();
5450         aItN = locMeshDS->GetNodes();
5451         while ( aItN->more() ) {
5452           const SMDS_MeshNode* pNode = aItN->next();
5453           const SMDS_EdgePosition* pEPos =
5454             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455           double aT = pEPos->GetUParameter();
5456           aPrms.push_back( aT );
5457         }
5458         list<SMESH_MeshEditor_PathPoint> LPP;
5459         //Extrusion_Error err =
5460         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461         LLPPs.push_back(LPP);
5462         UsedNums.Add(k);
5463         // update startN for search following egde
5464         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465         else startNid = aN1->GetID();
5466         break;
5467       }
5468     }
5469     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472     for(; itPP!=firstList.end(); itPP++) {
5473       fullList.push_back( *itPP );
5474     }
5475     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476     fullList.pop_back();
5477     itLLPP++;
5478     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480       itPP = currList.begin();
5481       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482       gp_Dir D1 = PP1.Tangent();
5483       gp_Dir D2 = PP2.Tangent();
5484       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485                            (D1.Z()+D2.Z())/2 ) );
5486       PP1.SetTangent(Dnew);
5487       fullList.push_back(PP1);
5488       itPP++;
5489       for(; itPP!=firstList.end(); itPP++) {
5490         fullList.push_back( *itPP );
5491       }
5492       PP1 = fullList.back();
5493       fullList.pop_back();
5494     }
5495     // if wire not closed
5496     fullList.push_back(PP1);
5497     // else ???
5498   }
5499   else {
5500     return EXTR_BAD_PATH_SHAPE;
5501   }
5502
5503   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504                           theHasRefPoint, theRefPoint, theMakeGroups);
5505 }
5506
5507
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5510 //purpose  :
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5514                                        SMESH_Mesh*          theTrack,
5515                                        const SMDS_MeshNode* theN1,
5516                                        const bool           theHasAngles,
5517                                        list<double>&        theAngles,
5518                                        const bool           theLinearVariation,
5519                                        const bool           theHasRefPoint,
5520                                        const gp_Pnt&        theRefPoint,
5521                                        const bool           theMakeGroups)
5522 {
5523   myLastCreatedElems.Clear();
5524   myLastCreatedNodes.Clear();
5525
5526   int aNbE;
5527   std::list<double> aPrms;
5528   TIDSortedElemSet::iterator itElem;
5529
5530   gp_XYZ aGC;
5531   TopoDS_Edge aTrackEdge;
5532   TopoDS_Vertex aV1, aV2;
5533
5534   SMDS_ElemIteratorPtr aItE;
5535   SMDS_NodeIteratorPtr aItN;
5536   SMDSAbs_ElementType aTypeE;
5537
5538   TNodeOfNodeListMap mapNewNodes;
5539
5540   // 1. Check data
5541   aNbE = theElements.size();
5542   // nothing to do
5543   if ( !aNbE )
5544     return EXTR_NO_ELEMENTS;
5545
5546   // 1.1 Track Pattern
5547   ASSERT( theTrack );
5548
5549   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5550
5551   aItE = pMeshDS->elementsIterator();
5552   while ( aItE->more() ) {
5553     const SMDS_MeshElement* pE = aItE->next();
5554     aTypeE = pE->GetType();
5555     // Pattern must contain links only
5556     if ( aTypeE != SMDSAbs_Edge )
5557       return EXTR_PATH_NOT_EDGE;
5558   }
5559
5560   list<SMESH_MeshEditor_PathPoint> fullList;
5561
5562   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5563
5564   if ( !theTrack->HasShapeToMesh() ) {
5565     //Mesh without shape
5566     const SMDS_MeshNode* currentNode = NULL;
5567     const SMDS_MeshNode* prevNode = theN1;
5568     std::vector<const SMDS_MeshNode*> aNodesList;
5569     aNodesList.push_back(theN1);
5570     int nbEdges = 0, conn=0;
5571     const SMDS_MeshElement* prevElem = NULL;
5572     const SMDS_MeshElement* currentElem = NULL;
5573     int totalNbEdges = theTrack->NbEdges();
5574     SMDS_ElemIteratorPtr nIt;
5575
5576     //check start node
5577     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578       return EXTR_BAD_STARTING_NODE;
5579     }
5580
5581     conn = nbEdgeConnectivity(theN1);
5582     if(conn > 2)
5583       return EXTR_PATH_NOT_EDGE;
5584
5585     aItE = theN1->GetInverseElementIterator();
5586     prevElem = aItE->next();
5587     currentElem = prevElem;
5588     //Get all nodes
5589     if(totalNbEdges == 1 ) {
5590       nIt = currentElem->nodesIterator();
5591       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592       if(currentNode == prevNode)
5593         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594       aNodesList.push_back(currentNode);
5595     } else {
5596       nIt = currentElem->nodesIterator();
5597       while( nIt->more() ) {
5598         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599         if(currentNode == prevNode)
5600           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601         aNodesList.push_back(currentNode);
5602
5603         //case of the closed mesh
5604         if(currentNode == theN1) {
5605           nbEdges++;
5606           break;
5607         }
5608
5609         conn = nbEdgeConnectivity(currentNode);
5610         if(conn > 2) {
5611           return EXTR_PATH_NOT_EDGE;
5612         }else if( conn == 1 && nbEdges > 0 ) {
5613           //End of the path
5614           nbEdges++;
5615           break;
5616         }else {
5617           prevNode = currentNode;
5618           aItE = currentNode->GetInverseElementIterator();
5619           currentElem = aItE->next();
5620           if( currentElem  == prevElem)
5621             currentElem = aItE->next();
5622           nIt = currentElem->nodesIterator();
5623           prevElem = currentElem;
5624           nbEdges++;
5625         }
5626       }
5627     }
5628
5629     if(nbEdges != totalNbEdges)
5630       return EXTR_PATH_NOT_EDGE;
5631
5632     TopTools_SequenceOfShape Edges;
5633     double x1,x2,y1,y2,z1,z2;
5634     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635     int startNid = theN1->GetID();
5636     for(int i = 1; i < aNodesList.size(); i++) {
5637       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641       list<SMESH_MeshEditor_PathPoint> LPP;
5642       aPrms.clear();
5643       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644       LLPPs.push_back(LPP);
5645       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646       else startNid = aNodesList[i-1]->GetID();
5647
5648     }
5649
5650     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653     for(; itPP!=firstList.end(); itPP++) {
5654       fullList.push_back( *itPP );
5655     }
5656
5657     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658     SMESH_MeshEditor_PathPoint PP2;
5659     fullList.pop_back();
5660     itLLPP++;
5661     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663       itPP = currList.begin();
5664       PP2 = currList.front();
5665       gp_Dir D1 = PP1.Tangent();
5666       gp_Dir D2 = PP2.Tangent();
5667       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668                            (D1.Z()+D2.Z())/2 ) );
5669       PP1.SetTangent(Dnew);
5670       fullList.push_back(PP1);
5671       itPP++;
5672       for(; itPP!=currList.end(); itPP++) {
5673         fullList.push_back( *itPP );
5674       }
5675       PP1 = fullList.back();
5676       fullList.pop_back();
5677     }
5678     fullList.push_back(PP1);
5679
5680   } // Sub-shape for the Pattern must be an Edge or Wire
5681   else if( aS.ShapeType() == TopAbs_EDGE ) {
5682     aTrackEdge = TopoDS::Edge( aS );
5683     // the Edge must not be degenerated
5684     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685       return EXTR_BAD_PATH_SHAPE;
5686     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689     // starting node must be aN1 or aN2
5690     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691       return EXTR_BAD_STARTING_NODE;
5692     aItN = pMeshDS->nodesIterator();
5693     while ( aItN->more() ) {
5694       const SMDS_MeshNode* pNode = aItN->next();
5695       if( pNode==aN1 || pNode==aN2 ) continue;
5696       const SMDS_EdgePosition* pEPos =
5697         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698       double aT = pEPos->GetUParameter();
5699       aPrms.push_back( aT );
5700     }
5701     //Extrusion_Error err =
5702     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5703   }
5704   else if( aS.ShapeType() == TopAbs_WIRE ) {
5705     list< SMESH_subMesh* > LSM;
5706     TopTools_SequenceOfShape Edges;
5707     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708     for(; eExp.More(); eExp.Next()) {
5709       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710       if( SMESH_Algo::isDegenerated(E) ) continue;
5711       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5712       if(SM) {
5713         LSM.push_back(SM);
5714         Edges.Append(E);
5715       }
5716     }
5717     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718     TopoDS_Vertex aVprev;
5719     TColStd_MapOfInteger UsedNums;
5720     int NbEdges = Edges.Length();
5721     int i = 1;
5722     for(; i<=NbEdges; i++) {
5723       int k = 0;
5724       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725       for(; itLSM!=LSM.end(); itLSM++) {
5726         k++;
5727         if(UsedNums.Contains(k)) continue;
5728         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729         SMESH_subMesh* locTrack = *itLSM;
5730         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732         bool aN1isOK = false, aN2isOK = false;
5733         if ( aVprev.IsNull() ) {
5734           // if previous vertex is not yet defined, it means that we in the beginning of wire
5735           // and we have to find initial vertex corresponding to starting node theN1
5736           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738           // starting node must be aN1 or aN2
5739           aN1isOK = ( aN1 && aN1 == theN1 );
5740           aN2isOK = ( aN2 && aN2 == theN1 );
5741         }
5742         else {
5743           // we have specified ending vertex of the previous edge on the previous iteration
5744           // and we have just to check that it corresponds to any vertex in current segment
5745           aN1isOK = aVprev.IsSame( aV1 );
5746           aN2isOK = aVprev.IsSame( aV2 );
5747         }
5748         if ( !aN1isOK && !aN2isOK ) continue;
5749         // 2. Collect parameters on the track edge
5750         aPrms.clear();
5751         aItN = locMeshDS->GetNodes();
5752         while ( aItN->more() ) {
5753           const SMDS_MeshNode*     pNode = aItN->next();
5754           const SMDS_EdgePosition* pEPos =
5755             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756           double aT = pEPos->GetUParameter();
5757           aPrms.push_back( aT );
5758         }
5759         list<SMESH_MeshEditor_PathPoint> LPP;
5760         //Extrusion_Error err =
5761         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762         LLPPs.push_back(LPP);
5763         UsedNums.Add(k);
5764         // update startN for search following egde
5765         if ( aN1isOK ) aVprev = aV2;
5766         else           aVprev = aV1;
5767         break;
5768       }
5769     }
5770     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772     fullList.splice( fullList.end(), firstList );
5773
5774     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775     fullList.pop_back();
5776     itLLPP++;
5777     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780       gp_Dir D1 = PP1.Tangent();
5781       gp_Dir D2 = PP2.Tangent();
5782       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783       PP1.SetTangent(Dnew);
5784       fullList.push_back(PP1);
5785       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786       PP1 = fullList.back();
5787       fullList.pop_back();
5788     }
5789     // if wire not closed
5790     fullList.push_back(PP1);
5791     // else ???
5792   }
5793   else {
5794     return EXTR_BAD_PATH_SHAPE;
5795   }
5796
5797   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798                           theHasRefPoint, theRefPoint, theMakeGroups);
5799 }
5800
5801
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose  : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5808                                      const TopoDS_Edge&                aTrackEdge,
5809                                      bool                              FirstIsStart,
5810                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5811 {
5812   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5813   aTolVec=1.e-7;
5814   aTolVec2=aTolVec*aTolVec;
5815   double aT1, aT2;
5816   TopoDS_Vertex aV1, aV2;
5817   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820   // 2. Collect parameters on the track edge
5821   aPrms.push_front( aT1 );
5822   aPrms.push_back( aT2 );
5823   // sort parameters
5824   aPrms.sort();
5825   if( FirstIsStart ) {
5826     if ( aT1 > aT2 ) {
5827       aPrms.reverse();
5828     }
5829   }
5830   else {
5831     if ( aT2 > aT1 ) {
5832       aPrms.reverse();
5833     }
5834   }
5835   // 3. Path Points
5836   SMESH_MeshEditor_PathPoint aPP;
5837   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838   std::list<double>::iterator aItD = aPrms.begin();
5839   for(; aItD != aPrms.end(); ++aItD) {
5840     double aT = *aItD;
5841     gp_Pnt aP3D;
5842     gp_Vec aVec;
5843     aC3D->D1( aT, aP3D, aVec );
5844     aL2 = aVec.SquareMagnitude();
5845     if ( aL2 < aTolVec2 )
5846       return EXTR_CANT_GET_TANGENT;
5847     gp_Dir aTgt( aVec );
5848     aPP.SetPnt( aP3D );
5849     aPP.SetTangent( aTgt );
5850     aPP.SetParameter( aT );
5851     LPP.push_back(aPP);
5852   }
5853   return EXTR_OK;
5854 }
5855
5856
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose  : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5863                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5864                                    const bool                        theHasAngles,
5865                                    list<double>&                     theAngles,
5866                                    const bool                        theLinearVariation,
5867                                    const bool                        theHasRefPoint,
5868                                    const gp_Pnt&                     theRefPoint,
5869                                    const bool                        theMakeGroups)
5870 {
5871   const int aNbTP = fullList.size();
5872   // Angles
5873   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874     LinearAngleVariation(aNbTP-1, theAngles);
5875   // fill vector of path points with angles
5876   vector<SMESH_MeshEditor_PathPoint> aPPs;
5877   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878   list<double>::iterator                 itAngles = theAngles.begin();
5879   aPPs.push_back( *itPP++ );
5880   for( ; itPP != fullList.end(); itPP++) {
5881     aPPs.push_back( *itPP );
5882     if ( theHasAngles && itAngles != theAngles.end() )
5883       aPPs.back().SetAngle( *itAngles++ );
5884   }
5885
5886   TNodeOfNodeListMap   mapNewNodes;
5887   TElemOfVecOfNnlmiMap mapElemNewNodes;
5888   TTElemOfElemListMap  newElemsMap;
5889   TIDSortedElemSet::iterator itElem;
5890   // source elements for each generated one
5891   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5892
5893   // 3. Center of rotation aV0
5894   gp_Pnt aV0 = theRefPoint;
5895   if ( !theHasRefPoint )
5896   {
5897     gp_XYZ aGC( 0.,0.,0. );
5898     TIDSortedElemSet newNodes;
5899
5900     itElem = theElements.begin();
5901     for ( ; itElem != theElements.end(); itElem++ ) {
5902       const SMDS_MeshElement* elem = *itElem;
5903
5904       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905       while ( itN->more() ) {
5906         const SMDS_MeshElement* node = itN->next();
5907         if ( newNodes.insert( node ).second )
5908           aGC += SMESH_TNodeXYZ( node );
5909       }
5910     }
5911     aGC /= newNodes.size();
5912     aV0.SetXYZ( aGC );
5913   } // if (!theHasRefPoint) {
5914
5915   // 4. Processing the elements
5916   SMESHDS_Mesh* aMesh = GetMeshDS();
5917
5918   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919     // check element type
5920     const SMDS_MeshElement* elem = *itElem;
5921     SMDSAbs_ElementType   aTypeE = elem->GetType();
5922     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5923       continue;
5924
5925     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926     newNodesItVec.reserve( elem->NbNodes() );
5927
5928     // loop on elem nodes
5929     int nodeIndex = -1;
5930     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931     while ( itN->more() )
5932     {
5933       ++nodeIndex;
5934       // check if a node has been already processed
5935       const SMDS_MeshNode* node =
5936         static_cast<const SMDS_MeshNode*>( itN->next() );
5937       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938       if ( nIt == mapNewNodes.end() ) {
5939         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5941
5942         // make new nodes
5943         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945         gp_Ax1 anAx1, anAxT1T0;
5946         gp_Dir aDT1x, aDT0x, aDT1T0;
5947
5948         aTolAng=1.e-4;
5949
5950         aV0x = aV0;
5951         aPN0 = SMESH_TNodeXYZ( node );
5952
5953         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5954         aP0x = aPP0.Pnt();
5955         aDT0x= aPP0.Tangent();
5956         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5957
5958         for ( int j = 1; j < aNbTP; ++j ) {
5959           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5960           aP1x     = aPP1.Pnt();
5961           aDT1x    = aPP1.Tangent();
5962           aAngle1x = aPP1.Angle();
5963
5964           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5965           // Translation
5966           gp_Vec aV01x( aP0x, aP1x );
5967           aTrsf.SetTranslation( aV01x );
5968
5969           // traslated point
5970           aV1x = aV0x.Transformed( aTrsf );
5971           aPN1 = aPN0.Transformed( aTrsf );
5972
5973           // rotation 1 [ T1,T0 ]
5974           aAngleT1T0=-aDT1x.Angle( aDT0x );
5975           if (fabs(aAngleT1T0) > aTolAng) {
5976             aDT1T0=aDT1x^aDT0x;
5977             anAxT1T0.SetLocation( aV1x );
5978             anAxT1T0.SetDirection( aDT1T0 );
5979             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5980
5981             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5982           }
5983
5984           // rotation 2
5985           if ( theHasAngles ) {
5986             anAx1.SetLocation( aV1x );
5987             anAx1.SetDirection( aDT1x );
5988             aTrsfRot.SetRotation( anAx1, aAngle1x );
5989
5990             aPN1 = aPN1.Transformed( aTrsfRot );
5991           }
5992
5993           // make new node
5994           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996             // create additional node
5997             double x = ( aPN1.X() + aPN0.X() )/2.;
5998             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001             myLastCreatedNodes.Append(newNode);
6002             srcNodes.Append( node );
6003             listNewNodes.push_back( newNode );
6004           }
6005           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006           myLastCreatedNodes.Append(newNode);
6007           srcNodes.Append( node );
6008           listNewNodes.push_back( newNode );
6009
6010           aPN0 = aPN1;
6011           aP0x = aP1x;
6012           aV0x = aV1x;
6013           aDT0x = aDT1x;
6014         }
6015       }
6016
6017       else {
6018         // if current elem is quadratic and current node is not medium
6019         // we have to check - may be it is needed to insert additional nodes
6020         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022           if(listNewNodes.size()==aNbTP-1) {
6023             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024             gp_XYZ P(node->X(), node->Y(), node->Z());
6025             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6026             int i;
6027             for(i=0; i<aNbTP-1; i++) {
6028               const SMDS_MeshNode* N = *it;
6029               double x = ( N->X() + P.X() )/2.;
6030               double y = ( N->Y() + P.Y() )/2.;
6031               double z = ( N->Z() + P.Z() )/2.;
6032               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033               srcNodes.Append( node );
6034               myLastCreatedNodes.Append(newN);
6035               aNodes[2*i] = newN;
6036               aNodes[2*i+1] = N;
6037               P = gp_XYZ(N->X(),N->Y(),N->Z());
6038             }
6039             listNewNodes.clear();
6040             for(i=0; i<2*(aNbTP-1); i++) {
6041               listNewNodes.push_back(aNodes[i]);
6042             }
6043           }
6044         }
6045       }
6046
6047       newNodesItVec.push_back( nIt );
6048     }
6049     // make new elements
6050     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6052     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6053   }
6054
6055   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6056
6057   if ( theMakeGroups )
6058     generateGroups( srcNodes, srcElems, "extruded");
6059
6060   return EXTR_OK;
6061 }
6062
6063
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose  : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069                                             list<double>& Angles)
6070 {
6071   int nbAngles = Angles.size();
6072   if( nbSteps > nbAngles ) {
6073     vector<double> theAngles(nbAngles);
6074     list<double>::iterator it = Angles.begin();
6075     int i = -1;
6076     for(; it!=Angles.end(); it++) {
6077       i++;
6078       theAngles[i] = (*it);
6079     }
6080     list<double> res;
6081     double rAn2St = double( nbAngles ) / double( nbSteps );
6082     double angPrev = 0, angle;
6083     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084       double angCur = rAn2St * ( iSt+1 );
6085       double angCurFloor  = floor( angCur );
6086       double angPrevFloor = floor( angPrev );
6087       if ( angPrevFloor == angCurFloor )
6088         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6089       else {
6090         int iP = int( angPrevFloor );
6091         double angPrevCeil = ceil(angPrev);
6092         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6093
6094         int iC = int( angCurFloor );
6095         if ( iC < nbAngles )
6096           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6097
6098         iP = int( angPrevCeil );
6099         while ( iC-- > iP )
6100           angle += theAngles[ iC ];
6101       }
6102       res.push_back(angle);
6103       angPrev = angCur;
6104     }
6105     Angles.clear();
6106     it = res.begin();
6107     for(; it!=res.end(); it++)
6108       Angles.push_back( *it );
6109   }
6110 }
6111
6112
6113 //================================================================================
6114 /*!
6115  * \brief Move or copy theElements applying theTrsf to their nodes
6116  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117  *  \param theTrsf - transformation to apply
6118  *  \param theCopy - if true, create translated copies of theElems
6119  *  \param theMakeGroups - if true and theCopy, create translated groups
6120  *  \param theTargetMesh - mesh to copy translated elements into
6121  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6122  */
6123 //================================================================================
6124
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127                              const gp_Trsf&     theTrsf,
6128                              const bool         theCopy,
6129                              const bool         theMakeGroups,
6130                              SMESH_Mesh*        theTargetMesh)
6131 {
6132   myLastCreatedElems.Clear();
6133   myLastCreatedNodes.Clear();
6134
6135   bool needReverse = false;
6136   string groupPostfix;
6137   switch ( theTrsf.Form() ) {
6138   case gp_PntMirror:
6139     MESSAGE("gp_PntMirror");
6140     needReverse = true;
6141     groupPostfix = "mirrored";
6142     break;
6143   case gp_Ax1Mirror:
6144     MESSAGE("gp_Ax1Mirror");
6145     groupPostfix = "mirrored";
6146     break;
6147   case gp_Ax2Mirror:
6148     MESSAGE("gp_Ax2Mirror");
6149     needReverse = true;
6150     groupPostfix = "mirrored";
6151     break;
6152   case gp_Rotation:
6153     MESSAGE("gp_Rotation");
6154     groupPostfix = "rotated";
6155     break;
6156   case gp_Translation:
6157     MESSAGE("gp_Translation");
6158     groupPostfix = "translated";
6159     break;
6160   case gp_Scale:
6161     MESSAGE("gp_Scale");
6162     groupPostfix = "scaled";
6163     break;
6164   case gp_CompoundTrsf: // different scale by axis
6165     MESSAGE("gp_CompoundTrsf");
6166     groupPostfix = "scaled";
6167     break;
6168   default:
6169     MESSAGE("default");
6170     needReverse = false;
6171     groupPostfix = "transformed";
6172   }
6173
6174   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176   SMESHDS_Mesh* aMesh    = GetMeshDS();
6177
6178
6179   // map old node to new one
6180   TNodeNodeMap nodeMap;
6181
6182   // elements sharing moved nodes; those of them which have all
6183   // nodes mirrored but are not in theElems are to be reversed
6184   TIDSortedElemSet inverseElemSet;
6185
6186   // source elements for each generated one
6187   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6188
6189   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190   TIDSortedElemSet orphanNode;
6191
6192   if ( theElems.empty() ) // transform the whole mesh
6193   {
6194     // add all elements
6195     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196     while ( eIt->more() ) theElems.insert( eIt->next() );
6197     // add orphan nodes
6198     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199     while ( nIt->more() )
6200     {
6201       const SMDS_MeshNode* node = nIt->next();
6202       if ( node->NbInverseElements() == 0)
6203         orphanNode.insert( node );
6204     }
6205   }
6206
6207   // loop on elements to transform nodes : first orphan nodes then elems
6208   TIDSortedElemSet::iterator itElem;
6209   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210   for (int i=0; i<2; i++)
6211   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212     const SMDS_MeshElement* elem = *itElem;
6213     if ( !elem )
6214       continue;
6215
6216     // loop on elem nodes
6217     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218     while ( itN->more() ) {
6219
6220       const SMDS_MeshNode* node = cast2Node( itN->next() );
6221       // check if a node has been already transformed
6222       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223         nodeMap.insert( make_pair ( node, node ));
6224       if ( !n2n_isnew.second )
6225         continue;
6226
6227       double coord[3];
6228       coord[0] = node->X();
6229       coord[1] = node->Y();
6230       coord[2] = node->Z();
6231       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232       if ( theTargetMesh ) {
6233         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234         n2n_isnew.first->second = newNode;
6235         myLastCreatedNodes.Append(newNode);
6236         srcNodes.Append( node );
6237       }
6238       else if ( theCopy ) {
6239         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240         n2n_isnew.first->second = newNode;
6241         myLastCreatedNodes.Append(newNode);
6242         srcNodes.Append( node );
6243       }
6244       else {
6245         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246         // node position on shape becomes invalid
6247         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248           ( SMDS_SpacePosition::originSpacePosition() );
6249       }
6250
6251       // keep inverse elements
6252       if ( !theCopy && !theTargetMesh && needReverse ) {
6253         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254         while ( invElemIt->more() ) {
6255           const SMDS_MeshElement* iel = invElemIt->next();
6256           inverseElemSet.insert( iel );
6257         }
6258       }
6259     }
6260   }
6261
6262   // either create new elements or reverse mirrored ones
6263   if ( !theCopy && !needReverse && !theTargetMesh )
6264     return PGroupIDs();
6265
6266   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268     theElems.insert( *invElemIt );
6269
6270   // Replicate or reverse elements
6271
6272   std::vector<int> iForw;
6273   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6274   {
6275     const SMDS_MeshElement* elem = *itElem;
6276     if ( !elem ) continue;
6277
6278     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279     int                  nbNodes  = elem->NbNodes();
6280     if ( geomType == SMDSGeom_NONE ) continue; // node
6281
6282     switch ( geomType ) {
6283
6284     case SMDSGeom_POLYGON:  // ---------------------- polygon
6285       {
6286         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6287         int iNode = 0;
6288         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289         while (itN->more()) {
6290           const SMDS_MeshNode* node =
6291             static_cast<const SMDS_MeshNode*>(itN->next());
6292           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293           if (nodeMapIt == nodeMap.end())
6294             break; // not all nodes transformed
6295           if (needReverse) {
6296             // reverse mirrored faces and volumes
6297             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6298           } else {
6299             poly_nodes[iNode] = (*nodeMapIt).second;
6300           }
6301           iNode++;
6302         }
6303         if ( iNode != nbNodes )
6304           continue; // not all nodes transformed
6305
6306         if ( theTargetMesh ) {
6307           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308           srcElems.Append( elem );
6309         }
6310         else if ( theCopy ) {
6311           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312           srcElems.Append( elem );
6313         }
6314         else {
6315           aMesh->ChangePolygonNodes(elem, poly_nodes);
6316         }
6317       }
6318       break;
6319
6320     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6321       {
6322         const SMDS_VtkVolume* aPolyedre =
6323           dynamic_cast<const SMDS_VtkVolume*>( elem );
6324         if (!aPolyedre) {
6325           MESSAGE("Warning: bad volumic element");
6326           continue;
6327         }
6328
6329         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330         vector<int> quantities; quantities.reserve( nbNodes );
6331
6332         bool allTransformed = true;
6333         int nbFaces = aPolyedre->NbFaces();
6334         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339             if (nodeMapIt == nodeMap.end()) {
6340               allTransformed = false; // not all nodes transformed
6341             } else {
6342               poly_nodes.push_back((*nodeMapIt).second);
6343             }
6344             if ( needReverse && allTransformed )
6345               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6346           }
6347           quantities.push_back(nbFaceNodes);
6348         }
6349         if ( !allTransformed )
6350           continue; // not all nodes transformed
6351
6352         if ( theTargetMesh ) {
6353           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354           srcElems.Append( elem );
6355         }
6356         else if ( theCopy ) {
6357           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358           srcElems.Append( elem );
6359         }
6360         else {
6361           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6362         }
6363       }
6364       break;
6365
6366     case SMDSGeom_BALL: // -------------------- Ball
6367       {
6368         if ( !theCopy && !theTargetMesh ) continue;
6369
6370         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371         if (nodeMapIt == nodeMap.end())
6372           continue; // not all nodes transformed
6373
6374         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375         if ( theTargetMesh ) {
6376           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377           srcElems.Append( elem );
6378         }
6379         else {
6380           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381           srcElems.Append( elem );
6382         }
6383       }
6384       break;
6385
6386     default: // ----------------------- Regular elements
6387
6388       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390       const std::vector<int>& i = needReverse ? iRev : iForw;
6391
6392       // find transformed nodes
6393       vector<const SMDS_MeshNode*> nodes(nbNodes);
6394       int iNode = 0;
6395       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396       while ( itN->more() ) {
6397         const SMDS_MeshNode* node =
6398           static_cast<const SMDS_MeshNode*>( itN->next() );
6399         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400         if ( nodeMapIt == nodeMap.end() )
6401           break; // not all nodes transformed
6402         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6403       }
6404       if ( iNode != nbNodes )
6405         continue; // not all nodes transformed
6406
6407       if ( theTargetMesh ) {
6408         if ( SMDS_MeshElement* copy =
6409              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410           myLastCreatedElems.Append( copy );
6411           srcElems.Append( elem );
6412         }
6413       }
6414       else if ( theCopy ) {
6415         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416           srcElems.Append( elem );
6417       }
6418       else {
6419         // reverse element as it was reversed by transformation
6420         if ( nbNodes > 2 )
6421           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6422       }
6423     } // switch ( geomType )
6424
6425   } // loop on elements
6426
6427   PGroupIDs newGroupIDs;
6428
6429   if ( ( theMakeGroups && theCopy ) ||
6430        ( theMakeGroups && theTargetMesh ) )
6431     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6432
6433   return newGroupIDs;
6434 }
6435
6436 //=======================================================================
6437 /*!
6438  * \brief Create groups of elements made during transformation
6439  * \param nodeGens - nodes making corresponding myLastCreatedNodes
6440  * \param elemGens - elements making corresponding myLastCreatedElems
6441  * \param postfix - to append to names of new groups
6442  */
6443 //=======================================================================
6444
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447                                  const SMESH_SequenceOfElemPtr& elemGens,
6448                                  const std::string&             postfix,
6449                                  SMESH_Mesh*                    targetMesh)
6450 {
6451   PGroupIDs newGroupIDs( new list<int> );
6452   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6453
6454   // Sort existing groups by types and collect their names
6455
6456   // containers to store an old group and generated new ones;
6457   // 1st new group is for result elems of different type than a source one;
6458   // 2nd new group is for same type result elems ("top" group at extrusion)
6459   using boost::tuple;
6460   using boost::make_tuple;
6461   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6462   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6463   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6464   // group names
6465   set< string > groupNames;
6466
6467   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6468   if ( !groupIt->more() ) return newGroupIDs;
6469
6470   int newGroupID = mesh->GetGroupIds().back()+1;
6471   while ( groupIt->more() )
6472   {
6473     SMESH_Group * group = groupIt->next();
6474     if ( !group ) continue;
6475     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6476     if ( !groupDS || groupDS->IsEmpty() ) continue;
6477     groupNames.insert    ( group->GetName() );
6478     groupDS->SetStoreName( group->GetName() );
6479     const SMDSAbs_ElementType type = groupDS->GetType();
6480     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6481     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6482     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6483     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6484   }
6485
6486   // Loop on nodes and elements to add them in new groups
6487
6488   // is there elements of different types generated from one source element;
6489   // it is false for transformations and true for sweeping
6490   bool isTwoTypesResult = false;
6491
6492   vector< const SMDS_MeshElement* > resultElems;
6493   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6494   {
6495     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6496     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6497     if ( gens.Length() != elems.Length() )
6498       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6499
6500     // loop on created elements
6501     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6502     {
6503       const SMDS_MeshElement* sourceElem = gens( iElem );
6504       if ( !sourceElem ) {
6505         MESSAGE("generateGroups(): NULL source element");
6506         continue;
6507       }
6508       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6509       if ( groupsOldNew.empty() ) { // no groups of this type at all
6510         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6511           ++iElem; // skip all elements made by sourceElem
6512         continue;
6513       }
6514       // collect all elements made by the iElem-th sourceElem
6515       resultElems.clear();
6516       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6517         if ( resElem != sourceElem )
6518           resultElems.push_back( resElem );
6519       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6520         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6521           if ( resElem != sourceElem )
6522             resultElems.push_back( resElem );
6523
6524       // there must be a top element
6525       const SMDS_MeshElement* topElem = 0;
6526       if ( isNodes )
6527       {
6528         topElem = resultElems.back();
6529         resultElems.pop_back();
6530       }
6531       else
6532       {
6533         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6534         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6535           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6536           {
6537             topElem = *resElemIt;
6538             *resElemIt = 0; // erase *resElemIt
6539             break;
6540           }
6541       }
6542
6543       // add resultElems to groups originted from ones the sourceElem belongs to
6544       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6545       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6546       {
6547         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6548         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6549         {
6550           // fill in a new group
6551           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6552           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6553           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6554             if ( *resElemIt )
6555               newGroup.Add( *resElemIt );
6556
6557           // fill a "top" group
6558           if ( topElem )
6559           {
6560             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6561             newTopGroup.Add( topElem );
6562
6563             if ( !newGroup.IsEmpty() )
6564               isTwoTypesResult = true;
6565           }
6566         }
6567       }
6568     } // loop on created elements
6569   }// loop on nodes and elements
6570
6571   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6572
6573   list<int> topGrouIds;
6574   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6575   {
6576     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6577     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6578                                       orderedOldNewGroups[i]->get<2>() };
6579     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6580     {
6581       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6582       if ( newGroupDS->IsEmpty() )
6583       {
6584         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6585       }
6586       else
6587       {
6588         // set group type
6589         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6590
6591         // make a name
6592         const bool isTop = ( isTwoTypesResult &&
6593                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6594                              is2nd );
6595
6596         string name = oldGroupDS->GetStoreName();
6597         { // remove trailing whitespaces (issue 22599)
6598           size_t size = name.size();
6599           while ( size > 1 && isspace( name[ size-1 ]))
6600             --size;
6601           if ( size != name.size() )
6602           {
6603             name.resize( size );
6604             oldGroupDS->SetStoreName( name.c_str() );
6605           }
6606         }
6607         if ( !targetMesh ) {
6608           string suffix = ( isTop ? "top": postfix.c_str() );
6609           name += "_";
6610           name += suffix;
6611           int nb = 1;
6612           while ( !groupNames.insert( name ).second ) // name exists
6613             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6614         }
6615         else if ( isTop ) {
6616           name += "_top";
6617         }
6618         newGroupDS->SetStoreName( name.c_str() );
6619
6620         // make a SMESH_Groups
6621         mesh->AddGroup( newGroupDS );
6622         if ( isTop )
6623           topGrouIds.push_back( newGroupDS->GetID() );
6624         else
6625           newGroupIDs->push_back( newGroupDS->GetID() );
6626       }
6627     }
6628   }
6629   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6630
6631   return newGroupIDs;
6632 }
6633
6634 //================================================================================
6635 /*!
6636  * \brief Return list of group of nodes close to each other within theTolerance
6637  *        Search among theNodes or in the whole mesh if theNodes is empty using
6638  *        an Octree algorithm
6639  */
6640 //================================================================================
6641
6642 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6643                                             const double         theTolerance,
6644                                             TListOfListOfNodes & theGroupsOfNodes)
6645 {
6646   myLastCreatedElems.Clear();
6647   myLastCreatedNodes.Clear();
6648
6649   if ( theNodes.empty() )
6650   { // get all nodes in the mesh
6651     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6652     while ( nIt->more() )
6653       theNodes.insert( theNodes.end(),nIt->next());
6654   }
6655
6656   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6657 }
6658
6659 //=======================================================================
6660 //function : SimplifyFace
6661 //purpose  :
6662 //=======================================================================
6663
6664 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6665                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6666                                     vector<int>&                         quantities) const
6667 {
6668   int nbNodes = faceNodes.size();
6669
6670   if (nbNodes < 3)
6671     return 0;
6672
6673   set<const SMDS_MeshNode*> nodeSet;
6674
6675   // get simple seq of nodes
6676   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6677   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6678   int iSimple = 0, nbUnique = 0;
6679
6680   simpleNodes[iSimple++] = faceNodes[0];
6681   nbUnique++;
6682   for (int iCur = 1; iCur < nbNodes; iCur++) {
6683     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6684       simpleNodes[iSimple++] = faceNodes[iCur];
6685       if (nodeSet.insert( faceNodes[iCur] ).second)
6686         nbUnique++;
6687     }
6688   }
6689   int nbSimple = iSimple;
6690   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6691     nbSimple--;
6692     iSimple--;
6693   }
6694
6695   if (nbUnique < 3)
6696     return 0;
6697
6698   // separate loops
6699   int nbNew = 0;
6700   bool foundLoop = (nbSimple > nbUnique);
6701   while (foundLoop) {
6702     foundLoop = false;
6703     set<const SMDS_MeshNode*> loopSet;
6704     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6705       const SMDS_MeshNode* n = simpleNodes[iSimple];
6706       if (!loopSet.insert( n ).second) {
6707         foundLoop = true;
6708
6709         // separate loop
6710         int iC = 0, curLast = iSimple;
6711         for (; iC < curLast; iC++) {
6712           if (simpleNodes[iC] == n) break;
6713         }
6714         int loopLen = curLast - iC;
6715         if (loopLen > 2) {
6716           // create sub-element
6717           nbNew++;
6718           quantities.push_back(loopLen);
6719           for (; iC < curLast; iC++) {
6720             poly_nodes.push_back(simpleNodes[iC]);
6721           }
6722         }
6723         // shift the rest nodes (place from the first loop position)
6724         for (iC = curLast + 1; iC < nbSimple; iC++) {
6725           simpleNodes[iC - loopLen] = simpleNodes[iC];
6726         }
6727         nbSimple -= loopLen;
6728         iSimple -= loopLen;
6729       }
6730     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6731   } // while (foundLoop)
6732
6733   if (iSimple > 2) {
6734     nbNew++;
6735     quantities.push_back(iSimple);
6736     for (int i = 0; i < iSimple; i++)
6737       poly_nodes.push_back(simpleNodes[i]);
6738   }
6739
6740   return nbNew;
6741 }
6742
6743 //=======================================================================
6744 //function : MergeNodes
6745 //purpose  : In each group, the cdr of nodes are substituted by the first one
6746 //           in all elements.
6747 //=======================================================================
6748
6749 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6750 {
6751   MESSAGE("MergeNodes");
6752   myLastCreatedElems.Clear();
6753   myLastCreatedNodes.Clear();
6754
6755   SMESHDS_Mesh* aMesh = GetMeshDS();
6756
6757   TNodeNodeMap nodeNodeMap; // node to replace - new node
6758   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6759   list< int > rmElemIds, rmNodeIds;
6760
6761   // Fill nodeNodeMap and elems
6762
6763   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6764   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6765     list<const SMDS_MeshNode*>& nodes = *grIt;
6766     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6767     const SMDS_MeshNode* nToKeep = *nIt;
6768     //MESSAGE("node to keep " << nToKeep->GetID());
6769     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6770       const SMDS_MeshNode* nToRemove = *nIt;
6771       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6772       if ( nToRemove != nToKeep ) {
6773         //MESSAGE("  node to remove " << nToRemove->GetID());
6774         rmNodeIds.push_back( nToRemove->GetID() );
6775         AddToSameGroups( nToKeep, nToRemove, aMesh );
6776         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6777         // after MergeNodes() w/o creating node in place of merged ones.
6778         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6779         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6780           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6781             sm->SetIsAlwaysComputed( true );
6782       }
6783
6784       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6785       while ( invElemIt->more() ) {
6786         const SMDS_MeshElement* elem = invElemIt->next();
6787         elems.insert(elem);
6788       }
6789     }
6790   }
6791   // Change element nodes or remove an element
6792
6793   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6794   for ( ; eIt != elems.end(); eIt++ ) {
6795     const SMDS_MeshElement* elem = *eIt;
6796     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6797     int nbNodes = elem->NbNodes();
6798     int aShapeId = FindShape( elem );
6799
6800     set<const SMDS_MeshNode*> nodeSet;
6801     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6802     int iUnique = 0, iCur = 0, nbRepl = 0;
6803     vector<int> iRepl( nbNodes );
6804
6805     // get new seq of nodes
6806     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6807     while ( itN->more() ) {
6808       const SMDS_MeshNode* n =
6809         static_cast<const SMDS_MeshNode*>( itN->next() );
6810
6811       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6812       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6813         n = (*nnIt).second;
6814         // BUG 0020185: begin
6815         {
6816           bool stopRecur = false;
6817           set<const SMDS_MeshNode*> nodesRecur;
6818           nodesRecur.insert(n);
6819           while (!stopRecur) {
6820             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6821             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6822               n = (*nnIt_i).second;
6823               if (!nodesRecur.insert(n).second) {
6824                 // error: recursive dependancy
6825                 stopRecur = true;
6826               }
6827             }
6828             else
6829               stopRecur = true;
6830           }
6831         }
6832         // BUG 0020185: end
6833       }
6834       curNodes[ iCur ] = n;
6835       bool isUnique = nodeSet.insert( n ).second;
6836       if ( isUnique )
6837         uniqueNodes[ iUnique++ ] = n;
6838       else
6839         iRepl[ nbRepl++ ] = iCur;
6840       iCur++;
6841     }
6842
6843     // Analyse element topology after replacement
6844
6845     bool isOk = true;
6846     int nbUniqueNodes = nodeSet.size();
6847     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6848     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6849       // Polygons and Polyhedral volumes
6850       if (elem->IsPoly()) {
6851
6852         if (elem->GetType() == SMDSAbs_Face) {
6853           // Polygon
6854           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6855           int inode = 0;
6856           for (; inode < nbNodes; inode++) {
6857             face_nodes[inode] = curNodes[inode];
6858           }
6859
6860           vector<const SMDS_MeshNode *> polygons_nodes;
6861           vector<int> quantities;
6862           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6863           if (nbNew > 0) {
6864             inode = 0;
6865             for (int iface = 0; iface < nbNew; iface++) {
6866               int nbNodes = quantities[iface];
6867               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6868               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6869                 poly_nodes[ii] = polygons_nodes[inode];
6870               }
6871               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6872               myLastCreatedElems.Append(newElem);
6873               if (aShapeId)
6874                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6875             }
6876
6877             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6878             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6879             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6880             int quid =0;
6881             if (nbNew > 0) quid = nbNew - 1;
6882             vector<int> newquant(quantities.begin()+quid, quantities.end());
6883             const SMDS_MeshElement* newElem = 0;
6884             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6885             myLastCreatedElems.Append(newElem);
6886             if ( aShapeId && newElem )
6887               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6888             rmElemIds.push_back(elem->GetID());
6889           }
6890           else {
6891             rmElemIds.push_back(elem->GetID());
6892           }
6893
6894         }
6895         else if (elem->GetType() == SMDSAbs_Volume) {
6896           // Polyhedral volume
6897           if (nbUniqueNodes < 4) {
6898             rmElemIds.push_back(elem->GetID());
6899           }
6900           else {
6901             // each face has to be analyzed in order to check volume validity
6902             const SMDS_VtkVolume* aPolyedre =
6903               dynamic_cast<const SMDS_VtkVolume*>( elem );
6904             if (aPolyedre) {
6905               int nbFaces = aPolyedre->NbFaces();
6906
6907               vector<const SMDS_MeshNode *> poly_nodes;
6908               vector<int> quantities;
6909
6910               for (int iface = 1; iface <= nbFaces; iface++) {
6911                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6912                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6913
6914                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6915                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6916                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6917                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6918                     faceNode = (*nnIt).second;
6919                   }
6920                   faceNodes[inode - 1] = faceNode;
6921                 }
6922
6923                 SimplifyFace(faceNodes, poly_nodes, quantities);
6924               }
6925
6926               if (quantities.size() > 3) {
6927                 // to be done: remove coincident faces
6928               }
6929
6930               if (quantities.size() > 3)
6931                 {
6932                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6933                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6934                   const SMDS_MeshElement* newElem = 0;
6935                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6936                   myLastCreatedElems.Append(newElem);
6937                   if ( aShapeId && newElem )
6938                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6939                   rmElemIds.push_back(elem->GetID());
6940                 }
6941             }
6942             else {
6943               rmElemIds.push_back(elem->GetID());
6944             }
6945           }
6946         }
6947         else {
6948         }
6949
6950         continue;
6951       } // poly element
6952
6953       // Regular elements
6954       // TODO not all the possible cases are solved. Find something more generic?
6955       switch ( nbNodes ) {
6956       case 2: ///////////////////////////////////// EDGE
6957         isOk = false; break;
6958       case 3: ///////////////////////////////////// TRIANGLE
6959         isOk = false; break;
6960       case 4:
6961         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6962           isOk = false;
6963         else { //////////////////////////////////// QUADRANGLE
6964           if ( nbUniqueNodes < 3 )
6965             isOk = false;
6966           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6967             isOk = false; // opposite nodes stick
6968           //MESSAGE("isOk " << isOk);
6969         }
6970         break;
6971       case 6: ///////////////////////////////////// PENTAHEDRON
6972         if ( nbUniqueNodes == 4 ) {
6973           // ---------------------------------> tetrahedron
6974           if (nbRepl == 3 &&
6975               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6976             // all top nodes stick: reverse a bottom
6977             uniqueNodes[ 0 ] = curNodes [ 1 ];
6978             uniqueNodes[ 1 ] = curNodes [ 0 ];
6979           }
6980           else if (nbRepl == 3 &&
6981                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6982             // all bottom nodes stick: set a top before
6983             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6984             uniqueNodes[ 0 ] = curNodes [ 3 ];
6985             uniqueNodes[ 1 ] = curNodes [ 4 ];
6986             uniqueNodes[ 2 ] = curNodes [ 5 ];
6987           }
6988           else if (nbRepl == 4 &&
6989                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6990             // a lateral face turns into a line: reverse a bottom
6991             uniqueNodes[ 0 ] = curNodes [ 1 ];
6992             uniqueNodes[ 1 ] = curNodes [ 0 ];
6993           }
6994           else
6995             isOk = false;
6996         }
6997         else if ( nbUniqueNodes == 5 ) {
6998           // PENTAHEDRON --------------------> 2 tetrahedrons
6999           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7000             // a bottom node sticks with a linked top one
7001             // 1.
7002             SMDS_MeshElement* newElem =
7003               aMesh->AddVolume(curNodes[ 3 ],
7004                                curNodes[ 4 ],
7005                                curNodes[ 5 ],
7006                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7007             myLastCreatedElems.Append(newElem);
7008             if ( aShapeId )
7009               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7010             // 2. : reverse a bottom
7011             uniqueNodes[ 0 ] = curNodes [ 1 ];
7012             uniqueNodes[ 1 ] = curNodes [ 0 ];
7013             nbUniqueNodes = 4;
7014           }
7015           else
7016             isOk = false;
7017         }
7018         else
7019           isOk = false;
7020         break;
7021       case 8: {
7022         if(elem->IsQuadratic()) { // Quadratic quadrangle
7023           //   1    5    2
7024           //    +---+---+
7025           //    |       |
7026           //    |       |
7027           //   4+       +6
7028           //    |       |
7029           //    |       |
7030           //    +---+---+
7031           //   0    7    3
7032           isOk = false;
7033           if(nbRepl==2) {
7034             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7035           }
7036           if(nbRepl==3) {
7037             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7038             nbUniqueNodes = 6;
7039             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7040               uniqueNodes[0] = curNodes[0];
7041               uniqueNodes[1] = curNodes[2];
7042               uniqueNodes[2] = curNodes[3];
7043               uniqueNodes[3] = curNodes[5];
7044               uniqueNodes[4] = curNodes[6];
7045               uniqueNodes[5] = curNodes[7];
7046               isOk = true;
7047             }
7048             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7049               uniqueNodes[0] = curNodes[0];
7050               uniqueNodes[1] = curNodes[1];
7051               uniqueNodes[2] = curNodes[2];
7052               uniqueNodes[3] = curNodes[4];
7053               uniqueNodes[4] = curNodes[5];
7054               uniqueNodes[5] = curNodes[6];
7055               isOk = true;
7056             }
7057             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7058               uniqueNodes[0] = curNodes[1];
7059               uniqueNodes[1] = curNodes[2];
7060               uniqueNodes[2] = curNodes[3];
7061               uniqueNodes[3] = curNodes[5];
7062               uniqueNodes[4] = curNodes[6];
7063               uniqueNodes[5] = curNodes[0];
7064               isOk = true;
7065             }
7066             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7067               uniqueNodes[0] = curNodes[0];
7068               uniqueNodes[1] = curNodes[1];
7069               uniqueNodes[2] = curNodes[3];
7070               uniqueNodes[3] = curNodes[4];
7071               uniqueNodes[4] = curNodes[6];
7072               uniqueNodes[5] = curNodes[7];
7073               isOk = true;
7074             }
7075             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7076               uniqueNodes[0] = curNodes[0];
7077               uniqueNodes[1] = curNodes[2];
7078               uniqueNodes[2] = curNodes[3];
7079               uniqueNodes[3] = curNodes[1];
7080               uniqueNodes[4] = curNodes[6];
7081               uniqueNodes[5] = curNodes[7];
7082               isOk = true;
7083             }
7084             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7085               uniqueNodes[0] = curNodes[0];
7086               uniqueNodes[1] = curNodes[1];
7087               uniqueNodes[2] = curNodes[2];
7088               uniqueNodes[3] = curNodes[4];
7089               uniqueNodes[4] = curNodes[5];
7090               uniqueNodes[5] = curNodes[7];
7091               isOk = true;
7092             }
7093             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7094               uniqueNodes[0] = curNodes[0];
7095               uniqueNodes[1] = curNodes[1];
7096               uniqueNodes[2] = curNodes[3];
7097               uniqueNodes[3] = curNodes[4];
7098               uniqueNodes[4] = curNodes[2];
7099               uniqueNodes[5] = curNodes[7];
7100               isOk = true;
7101             }
7102             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7103               uniqueNodes[0] = curNodes[0];
7104               uniqueNodes[1] = curNodes[1];
7105               uniqueNodes[2] = curNodes[2];
7106               uniqueNodes[3] = curNodes[4];
7107               uniqueNodes[4] = curNodes[5];
7108               uniqueNodes[5] = curNodes[3];
7109               isOk = true;
7110             }
7111           }
7112           if(nbRepl==4) {
7113             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7114           }
7115           if(nbRepl==5) {
7116             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7117           }
7118           break;
7119         }
7120         //////////////////////////////////// HEXAHEDRON
7121         isOk = false;
7122         SMDS_VolumeTool hexa (elem);
7123         hexa.SetExternalNormal();
7124         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7125           //////////////////////// HEX ---> 1 tetrahedron
7126           for ( int iFace = 0; iFace < 6; iFace++ ) {
7127             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7128             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7129                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7130                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7131               // one face turns into a point ...
7132               int iOppFace = hexa.GetOppFaceIndex( iFace );
7133               ind = hexa.GetFaceNodesIndices( iOppFace );
7134               int nbStick = 0;
7135               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7136                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7137                   nbStick++;
7138               }
7139               if ( nbStick == 1 ) {
7140                 // ... and the opposite one - into a triangle.
7141                 // set a top node
7142                 ind = hexa.GetFaceNodesIndices( iFace );
7143                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7144                 isOk = true;
7145               }
7146               break;
7147             }
7148           }
7149         }
7150         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7151           //////////////////////// HEX ---> 1 prism
7152           int nbTria = 0, iTria[3];
7153           const int *ind; // indices of face nodes
7154           // look for triangular faces
7155           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7156             ind = hexa.GetFaceNodesIndices( iFace );
7157             TIDSortedNodeSet faceNodes;
7158             for ( iCur = 0; iCur < 4; iCur++ )
7159               faceNodes.insert( curNodes[ind[iCur]] );
7160             if ( faceNodes.size() == 3 )
7161               iTria[ nbTria++ ] = iFace;
7162           }
7163           // check if triangles are opposite
7164           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7165           {
7166             isOk = true;
7167             // set nodes of the bottom triangle
7168             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7169             vector<int> indB;
7170             for ( iCur = 0; iCur < 4; iCur++ )
7171               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7172                 indB.push_back( ind[iCur] );
7173             if ( !hexa.IsForward() )
7174               std::swap( indB[0], indB[2] );
7175             for ( iCur = 0; iCur < 3; iCur++ )
7176               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7177             // set nodes of the top triangle
7178             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7179             for ( iCur = 0; iCur < 3; ++iCur )
7180               for ( int j = 0; j < 4; ++j )
7181                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7182                 {
7183                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7184                   break;
7185                 }
7186           }
7187           break;
7188         }
7189         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7190           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7191           for ( int iFace = 0; iFace < 6; iFace++ ) {
7192             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7193             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7194                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7195                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7196               // one face turns into a point ...
7197               int iOppFace = hexa.GetOppFaceIndex( iFace );
7198               ind = hexa.GetFaceNodesIndices( iOppFace );
7199               int nbStick = 0;
7200               iUnique = 2;  // reverse a tetrahedron 1 bottom
7201               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7202                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7203                   nbStick++;
7204                 else if ( iUnique >= 0 )
7205                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7206               }
7207               if ( nbStick == 0 ) {
7208                 // ... and the opposite one is a quadrangle
7209                 // set a top node
7210                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7211                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7212                 nbUniqueNodes = 4;
7213                 // tetrahedron 2
7214                 SMDS_MeshElement* newElem =
7215                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7216                                    curNodes[ind[ 3 ]],
7217                                    curNodes[ind[ 2 ]],
7218                                    curNodes[indTop[ 0 ]]);
7219                 myLastCreatedElems.Append(newElem);
7220                 if ( aShapeId )
7221                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7222                 isOk = true;
7223               }
7224               break;
7225             }
7226           }
7227         }
7228         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7229           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7230           // find indices of quad and tri faces
7231           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7232           for ( iFace = 0; iFace < 6; iFace++ ) {
7233             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7234             nodeSet.clear();
7235             for ( iCur = 0; iCur < 4; iCur++ )
7236               nodeSet.insert( curNodes[ind[ iCur ]] );
7237             nbUniqueNodes = nodeSet.size();
7238             if ( nbUniqueNodes == 3 )
7239               iTriFace[ nbTri++ ] = iFace;
7240             else if ( nbUniqueNodes == 4 )
7241               iQuadFace[ nbQuad++ ] = iFace;
7242           }
7243           if (nbQuad == 2 && nbTri == 4 &&
7244               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7245             // 2 opposite quadrangles stuck with a diagonal;
7246             // sample groups of merged indices: (0-4)(2-6)
7247             // --------------------------------------------> 2 tetrahedrons
7248             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7249             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7250             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7251             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7252                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7253               // stuck with 0-2 diagonal
7254               i0  = ind1[ 3 ];
7255               i1d = ind1[ 0 ];
7256               i2  = ind1[ 1 ];
7257               i3d = ind1[ 2 ];
7258               i0t = ind2[ 1 ];
7259               i2t = ind2[ 3 ];
7260             }
7261             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7262                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7263               // stuck with 1-3 diagonal
7264               i0  = ind1[ 0 ];
7265               i1d = ind1[ 1 ];
7266               i2  = ind1[ 2 ];
7267               i3d = ind1[ 3 ];
7268               i0t = ind2[ 0 ];
7269               i2t = ind2[ 1 ];
7270             }
7271             else {
7272               ASSERT(0);
7273             }
7274             // tetrahedron 1
7275             uniqueNodes[ 0 ] = curNodes [ i0 ];
7276             uniqueNodes[ 1 ] = curNodes [ i1d ];
7277             uniqueNodes[ 2 ] = curNodes [ i3d ];
7278             uniqueNodes[ 3 ] = curNodes [ i0t ];
7279             nbUniqueNodes = 4;
7280             // tetrahedron 2
7281             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7282                                                          curNodes[ i2 ],
7283                                                          curNodes[ i3d ],
7284                                                          curNodes[ i2t ]);
7285             myLastCreatedElems.Append(newElem);
7286             if ( aShapeId )
7287               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7288             isOk = true;
7289           }
7290           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7291                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7292             // --------------------------------------------> prism
7293             // find 2 opposite triangles
7294             nbUniqueNodes = 6;
7295             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7296               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7297                 // find indices of kept and replaced nodes
7298                 // and fill unique nodes of 2 opposite triangles
7299                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7300                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7301                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7302                 // fill unique nodes
7303                 iUnique = 0;
7304                 isOk = true;
7305                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7306                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7307                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7308                   if ( n == nInit ) {
7309                     // iCur of a linked node of the opposite face (make normals co-directed):
7310                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7311                     // check that correspondent corners of triangles are linked
7312                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7313                       isOk = false;
7314                     else {
7315                       uniqueNodes[ iUnique ] = n;
7316                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7317                       iUnique++;
7318                     }
7319                   }
7320                 }
7321                 break;
7322               }
7323             }
7324           }
7325         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7326         else
7327         {
7328           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7329         }
7330         break;
7331       } // HEXAHEDRON
7332
7333       default:
7334         isOk = false;
7335       } // switch ( nbNodes )
7336
7337     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7338
7339     if ( isOk ) { // the elem remains valid after sticking nodes
7340       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7341       {
7342         // Change nodes of polyedre
7343         const SMDS_VtkVolume* aPolyedre =
7344           dynamic_cast<const SMDS_VtkVolume*>( elem );
7345         if (aPolyedre) {
7346           int nbFaces = aPolyedre->NbFaces();
7347
7348           vector<const SMDS_MeshNode *> poly_nodes;
7349           vector<int> quantities (nbFaces);
7350
7351           for (int iface = 1; iface <= nbFaces; iface++) {
7352             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7353             quantities[iface - 1] = nbFaceNodes;
7354
7355             for (inode = 1; inode <= nbFaceNodes; inode++) {
7356               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7357
7358               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7359               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7360                 curNode = (*nnIt).second;
7361               }
7362               poly_nodes.push_back(curNode);
7363             }
7364           }
7365           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7366         }
7367       }
7368       else // replace non-polyhedron elements
7369       {
7370         const SMDSAbs_ElementType etyp = elem->GetType();
7371         const int elemId               = elem->GetID();
7372         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7373         uniqueNodes.resize(nbUniqueNodes);
7374
7375         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7376
7377         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7378         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7379         if ( sm && newElem )
7380           sm->AddElement( newElem );
7381         if ( elem != newElem )
7382           ReplaceElemInGroups( elem, newElem, aMesh );
7383       }
7384     }
7385     else {
7386       // Remove invalid regular element or invalid polygon
7387       rmElemIds.push_back( elem->GetID() );
7388     }
7389
7390   } // loop on elements
7391
7392   // Remove bad elements, then equal nodes (order important)
7393
7394   Remove( rmElemIds, false );
7395   Remove( rmNodeIds, true );
7396
7397 }
7398
7399
7400 // ========================================================
7401 // class   : SortableElement
7402 // purpose : allow sorting elements basing on their nodes
7403 // ========================================================
7404 class SortableElement : public set <const SMDS_MeshElement*>
7405 {
7406 public:
7407
7408   SortableElement( const SMDS_MeshElement* theElem )
7409   {
7410     myElem = theElem;
7411     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7412     while ( nodeIt->more() )
7413       this->insert( nodeIt->next() );
7414   }
7415
7416   const SMDS_MeshElement* Get() const
7417   { return myElem; }
7418
7419   void Set(const SMDS_MeshElement* e) const
7420   { myElem = e; }
7421
7422
7423 private:
7424   mutable const SMDS_MeshElement* myElem;
7425 };
7426
7427 //=======================================================================
7428 //function : FindEqualElements
7429 //purpose  : Return list of group of elements built on the same nodes.
7430 //           Search among theElements or in the whole mesh if theElements is empty
7431 //=======================================================================
7432
7433 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7434                                          TListOfListOfElementsID & theGroupsOfElementsID)
7435 {
7436   myLastCreatedElems.Clear();
7437   myLastCreatedNodes.Clear();
7438
7439   typedef map< SortableElement, int > TMapOfNodeSet;
7440   typedef list<int> TGroupOfElems;
7441
7442   if ( theElements.empty() )
7443   { // get all elements in the mesh
7444     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7445     while ( eIt->more() )
7446       theElements.insert( theElements.end(), eIt->next());
7447   }
7448
7449   vector< TGroupOfElems > arrayOfGroups;
7450   TGroupOfElems groupOfElems;
7451   TMapOfNodeSet mapOfNodeSet;
7452
7453   TIDSortedElemSet::iterator elemIt = theElements.begin();
7454   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7455     const SMDS_MeshElement* curElem = *elemIt;
7456     SortableElement SE(curElem);
7457     int ind = -1;
7458     // check uniqueness
7459     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7460     if( !(pp.second) ) {
7461       TMapOfNodeSet::iterator& itSE = pp.first;
7462       ind = (*itSE).second;
7463       arrayOfGroups[ind].push_back(curElem->GetID());
7464     }
7465     else {
7466       groupOfElems.clear();
7467       groupOfElems.push_back(curElem->GetID());
7468       arrayOfGroups.push_back(groupOfElems);
7469       i++;
7470     }
7471   }
7472
7473   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7474   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7475     groupOfElems = *groupIt;
7476     if ( groupOfElems.size() > 1 ) {
7477       groupOfElems.sort();
7478       theGroupsOfElementsID.push_back(groupOfElems);
7479     }
7480   }
7481 }
7482
7483 //=======================================================================
7484 //function : MergeElements
7485 //purpose  : In each given group, substitute all elements by the first one.
7486 //=======================================================================
7487
7488 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7489 {
7490   myLastCreatedElems.Clear();
7491   myLastCreatedNodes.Clear();
7492
7493   typedef list<int> TListOfIDs;
7494   TListOfIDs rmElemIds; // IDs of elems to remove
7495
7496   SMESHDS_Mesh* aMesh = GetMeshDS();
7497
7498   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7499   while ( groupsIt != theGroupsOfElementsID.end() ) {
7500     TListOfIDs& aGroupOfElemID = *groupsIt;
7501     aGroupOfElemID.sort();
7502     int elemIDToKeep = aGroupOfElemID.front();
7503     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7504     aGroupOfElemID.pop_front();
7505     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7506     while ( idIt != aGroupOfElemID.end() ) {
7507       int elemIDToRemove = *idIt;
7508       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7509       // add the kept element in groups of removed one (PAL15188)
7510       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7511       rmElemIds.push_back( elemIDToRemove );
7512       ++idIt;
7513     }
7514     ++groupsIt;
7515   }
7516
7517   Remove( rmElemIds, false );
7518 }
7519
7520 //=======================================================================
7521 //function : MergeEqualElements
7522 //purpose  : Remove all but one of elements built on the same nodes.
7523 //=======================================================================
7524
7525 void SMESH_MeshEditor::MergeEqualElements()
7526 {
7527   TIDSortedElemSet aMeshElements; /* empty input ==
7528                                      to merge equal elements in the whole mesh */
7529   TListOfListOfElementsID aGroupsOfElementsID;
7530   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7531   MergeElements(aGroupsOfElementsID);
7532 }
7533
7534 //=======================================================================
7535 //function : findAdjacentFace
7536 //purpose  :
7537 //=======================================================================
7538
7539 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7540                                                 const SMDS_MeshNode* n2,
7541                                                 const SMDS_MeshElement* elem)
7542 {
7543   TIDSortedElemSet elemSet, avoidSet;
7544   if ( elem )
7545     avoidSet.insert ( elem );
7546   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7547 }
7548
7549 //=======================================================================
7550 //function : FindFreeBorder
7551 //purpose  :
7552 //=======================================================================
7553
7554 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7555
7556 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7557                                        const SMDS_MeshNode*             theSecondNode,
7558                                        const SMDS_MeshNode*             theLastNode,
7559                                        list< const SMDS_MeshNode* > &   theNodes,
7560                                        list< const SMDS_MeshElement* >& theFaces)
7561 {
7562   if ( !theFirstNode || !theSecondNode )
7563     return false;
7564   // find border face between theFirstNode and theSecondNode
7565   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7566   if ( !curElem )
7567     return false;
7568
7569   theFaces.push_back( curElem );
7570   theNodes.push_back( theFirstNode );
7571   theNodes.push_back( theSecondNode );
7572
7573   //vector<const SMDS_MeshNode*> nodes;
7574   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7575   TIDSortedElemSet foundElems;
7576   bool needTheLast = ( theLastNode != 0 );
7577
7578   while ( nStart != theLastNode ) {
7579     if ( nStart == theFirstNode )
7580       return !needTheLast;
7581
7582     // find all free border faces sharing form nStart
7583
7584     list< const SMDS_MeshElement* > curElemList;
7585     list< const SMDS_MeshNode* > nStartList;
7586     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7587     while ( invElemIt->more() ) {
7588       const SMDS_MeshElement* e = invElemIt->next();
7589       if ( e == curElem || foundElems.insert( e ).second ) {
7590         // get nodes
7591         int iNode = 0, nbNodes = e->NbNodes();
7592         //const SMDS_MeshNode* nodes[nbNodes+1];
7593         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7594
7595         if(e->IsQuadratic()) {
7596           const SMDS_VtkFace* F =
7597             dynamic_cast<const SMDS_VtkFace*>(e);
7598           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7599           // use special nodes iterator
7600           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7601           while( anIter->more() ) {
7602             nodes[ iNode++ ] = cast2Node(anIter->next());
7603           }
7604         }
7605         else {
7606           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7607           while ( nIt->more() )
7608             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7609         }
7610         nodes[ iNode ] = nodes[ 0 ];
7611         // check 2 links
7612         for ( iNode = 0; iNode < nbNodes; iNode++ )
7613           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7614                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7615               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7616           {
7617             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7618             curElemList.push_back( e );
7619           }
7620       }
7621     }
7622     // analyse the found
7623
7624     int nbNewBorders = curElemList.size();
7625     if ( nbNewBorders == 0 ) {
7626       // no free border furthermore
7627       return !needTheLast;
7628     }
7629     else if ( nbNewBorders == 1 ) {
7630       // one more element found
7631       nIgnore = nStart;
7632       nStart = nStartList.front();
7633       curElem = curElemList.front();
7634       theFaces.push_back( curElem );
7635       theNodes.push_back( nStart );
7636     }
7637     else {
7638       // several continuations found
7639       list< const SMDS_MeshElement* >::iterator curElemIt;
7640       list< const SMDS_MeshNode* >::iterator nStartIt;
7641       // check if one of them reached the last node
7642       if ( needTheLast ) {
7643         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7644              curElemIt!= curElemList.end();
7645              curElemIt++, nStartIt++ )
7646           if ( *nStartIt == theLastNode ) {
7647             theFaces.push_back( *curElemIt );
7648             theNodes.push_back( *nStartIt );
7649             return true;
7650           }
7651       }
7652       // find the best free border by the continuations
7653       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7654       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7655       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7656            curElemIt!= curElemList.end();
7657            curElemIt++, nStartIt++ )
7658       {
7659         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7660         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7661         // find one more free border
7662         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7663           cNL->clear();
7664           cFL->clear();
7665         }
7666         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7667           // choice: clear a worse one
7668           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7669           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7670           contNodes[ iWorse ].clear();
7671           contFaces[ iWorse ].clear();
7672         }
7673       }
7674       if ( contNodes[0].empty() && contNodes[1].empty() )
7675         return false;
7676
7677       // append the best free border
7678       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7679       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7680       theNodes.pop_back(); // remove nIgnore
7681       theNodes.pop_back(); // remove nStart
7682       theFaces.pop_back(); // remove curElem
7683       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7684       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7685       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7686       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7687       return true;
7688
7689     } // several continuations found
7690   } // while ( nStart != theLastNode )
7691
7692   return true;
7693 }
7694
7695 //=======================================================================
7696 //function : CheckFreeBorderNodes
7697 //purpose  : Return true if the tree nodes are on a free border
7698 //=======================================================================
7699
7700 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7701                                             const SMDS_MeshNode* theNode2,
7702                                             const SMDS_MeshNode* theNode3)
7703 {
7704   list< const SMDS_MeshNode* > nodes;
7705   list< const SMDS_MeshElement* > faces;
7706   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7707 }
7708
7709 //=======================================================================
7710 //function : SewFreeBorder
7711 //purpose  :
7712 //=======================================================================
7713
7714 SMESH_MeshEditor::Sew_Error
7715 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7716                                  const SMDS_MeshNode* theBordSecondNode,
7717                                  const SMDS_MeshNode* theBordLastNode,
7718                                  const SMDS_MeshNode* theSideFirstNode,
7719                                  const SMDS_MeshNode* theSideSecondNode,
7720                                  const SMDS_MeshNode* theSideThirdNode,
7721                                  const bool           theSideIsFreeBorder,
7722                                  const bool           toCreatePolygons,
7723                                  const bool           toCreatePolyedrs)
7724 {
7725   myLastCreatedElems.Clear();
7726   myLastCreatedNodes.Clear();
7727
7728   MESSAGE("::SewFreeBorder()");
7729   Sew_Error aResult = SEW_OK;
7730
7731   // ====================================
7732   //    find side nodes and elements
7733   // ====================================
7734
7735   list< const SMDS_MeshNode* > nSide[ 2 ];
7736   list< const SMDS_MeshElement* > eSide[ 2 ];
7737   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7738   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7739
7740   // Free border 1
7741   // --------------
7742   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7743                       nSide[0], eSide[0])) {
7744     MESSAGE(" Free Border 1 not found " );
7745     aResult = SEW_BORDER1_NOT_FOUND;
7746   }
7747   if (theSideIsFreeBorder) {
7748     // Free border 2
7749     // --------------
7750     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7751                         nSide[1], eSide[1])) {
7752       MESSAGE(" Free Border 2 not found " );
7753       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7754     }
7755   }
7756   if ( aResult != SEW_OK )
7757     return aResult;
7758
7759   if (!theSideIsFreeBorder) {
7760     // Side 2
7761     // --------------
7762
7763     // -------------------------------------------------------------------------
7764     // Algo:
7765     // 1. If nodes to merge are not coincident, move nodes of the free border
7766     //    from the coord sys defined by the direction from the first to last
7767     //    nodes of the border to the correspondent sys of the side 2
7768     // 2. On the side 2, find the links most co-directed with the correspondent
7769     //    links of the free border
7770     // -------------------------------------------------------------------------
7771
7772     // 1. Since sewing may break if there are volumes to split on the side 2,
7773     //    we wont move nodes but just compute new coordinates for them
7774     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7775     TNodeXYZMap nBordXYZ;
7776     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7777     list< const SMDS_MeshNode* >::iterator nBordIt;
7778
7779     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7780     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7781     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7782     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7783     double tol2 = 1.e-8;
7784     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7785     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7786       // Need node movement.
7787
7788       // find X and Z axes to create trsf
7789       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7790       gp_Vec X = Zs ^ Zb;
7791       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7792         // Zb || Zs
7793         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7794
7795       // coord systems
7796       gp_Ax3 toBordAx( Pb1, Zb, X );
7797       gp_Ax3 fromSideAx( Ps1, Zs, X );
7798       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7799       // set trsf
7800       gp_Trsf toBordSys, fromSide2Sys;
7801       toBordSys.SetTransformation( toBordAx );
7802       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7803       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7804
7805       // move
7806       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7807         const SMDS_MeshNode* n = *nBordIt;
7808         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7809         toBordSys.Transforms( xyz );
7810         fromSide2Sys.Transforms( xyz );
7811         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7812       }
7813     }
7814     else {
7815       // just insert nodes XYZ in the nBordXYZ map
7816       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7817         const SMDS_MeshNode* n = *nBordIt;
7818         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7819       }
7820     }
7821
7822     // 2. On the side 2, find the links most co-directed with the correspondent
7823     //    links of the free border
7824
7825     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7826     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7827     sideNodes.push_back( theSideFirstNode );
7828
7829     bool hasVolumes = false;
7830     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7831     set<long> foundSideLinkIDs, checkedLinkIDs;
7832     SMDS_VolumeTool volume;
7833     //const SMDS_MeshNode* faceNodes[ 4 ];
7834
7835     const SMDS_MeshNode*    sideNode;
7836     const SMDS_MeshElement* sideElem;
7837     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7838     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7839     nBordIt = bordNodes.begin();
7840     nBordIt++;
7841     // border node position and border link direction to compare with
7842     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7843     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7844     // choose next side node by link direction or by closeness to
7845     // the current border node:
7846     bool searchByDir = ( *nBordIt != theBordLastNode );
7847     do {
7848       // find the next node on the Side 2
7849       sideNode = 0;
7850       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7851       long linkID;
7852       checkedLinkIDs.clear();
7853       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7854
7855       // loop on inverse elements of current node (prevSideNode) on the Side 2
7856       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7857       while ( invElemIt->more() )
7858       {
7859         const SMDS_MeshElement* elem = invElemIt->next();
7860         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7861         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7862         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7863         bool isVolume = volume.Set( elem );
7864         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7865         if ( isVolume ) // --volume
7866           hasVolumes = true;
7867         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7868           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7869           if(elem->IsQuadratic()) {
7870             const SMDS_VtkFace* F =
7871               dynamic_cast<const SMDS_VtkFace*>(elem);
7872             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7873             // use special nodes iterator
7874             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7875             while( anIter->more() ) {
7876               nodes[ iNode ] = cast2Node(anIter->next());
7877               if ( nodes[ iNode++ ] == prevSideNode )
7878                 iPrevNode = iNode - 1;
7879             }
7880           }
7881           else {
7882             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7883             while ( nIt->more() ) {
7884               nodes[ iNode ] = cast2Node( nIt->next() );
7885               if ( nodes[ iNode++ ] == prevSideNode )
7886                 iPrevNode = iNode - 1;
7887             }
7888           }
7889           // there are 2 links to check
7890           nbNodes = 2;
7891         }
7892         else // --edge
7893           continue;
7894         // loop on links, to be precise, on the second node of links
7895         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7896           const SMDS_MeshNode* n = nodes[ iNode ];
7897           if ( isVolume ) {
7898             if ( !volume.IsLinked( n, prevSideNode ))
7899               continue;
7900           }
7901           else {
7902             if ( iNode ) // a node before prevSideNode
7903               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7904             else         // a node after prevSideNode
7905               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7906           }
7907           // check if this link was already used
7908           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7909           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7910           if (!isJustChecked &&
7911               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7912           {
7913             // test a link geometrically
7914             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7915             bool linkIsBetter = false;
7916             double dot = 0.0, dist = 0.0;
7917             if ( searchByDir ) { // choose most co-directed link
7918               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7919               linkIsBetter = ( dot > maxDot );
7920             }
7921             else { // choose link with the node closest to bordPos
7922               dist = ( nextXYZ - bordPos ).SquareModulus();
7923               linkIsBetter = ( dist < minDist );
7924             }
7925             if ( linkIsBetter ) {
7926               maxDot = dot;
7927               minDist = dist;
7928               linkID = iLink;
7929               sideNode = n;
7930               sideElem = elem;
7931             }
7932           }
7933         }
7934       } // loop on inverse elements of prevSideNode
7935
7936       if ( !sideNode ) {
7937         MESSAGE(" Cant find path by links of the Side 2 ");
7938         return SEW_BAD_SIDE_NODES;
7939       }
7940       sideNodes.push_back( sideNode );
7941       sideElems.push_back( sideElem );
7942       foundSideLinkIDs.insert ( linkID );
7943       prevSideNode = sideNode;
7944
7945       if ( *nBordIt == theBordLastNode )
7946         searchByDir = false;
7947       else {
7948         // find the next border link to compare with
7949         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7950         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7951         // move to next border node if sideNode is before forward border node (bordPos)
7952         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7953           prevBordNode = *nBordIt;
7954           nBordIt++;
7955           bordPos = nBordXYZ[ *nBordIt ];
7956           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7957           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7958         }
7959       }
7960     }
7961     while ( sideNode != theSideSecondNode );
7962
7963     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7964       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7965       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7966     }
7967   } // end nodes search on the side 2
7968
7969   // ============================
7970   // sew the border to the side 2
7971   // ============================
7972
7973   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7974   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7975
7976   TListOfListOfNodes nodeGroupsToMerge;
7977   if ( nbNodes[0] == nbNodes[1] ||
7978        ( theSideIsFreeBorder && !theSideThirdNode)) {
7979
7980     // all nodes are to be merged
7981
7982     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7983          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7984          nIt[0]++, nIt[1]++ )
7985     {
7986       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7987       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7988       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7989     }
7990   }
7991   else {
7992
7993     // insert new nodes into the border and the side to get equal nb of segments
7994
7995     // get normalized parameters of nodes on the borders
7996     //double param[ 2 ][ maxNbNodes ];
7997     double* param[ 2 ];
7998     param[0] = new double [ maxNbNodes ];
7999     param[1] = new double [ maxNbNodes ];
8000     int iNode, iBord;
8001     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8002       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8003       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8004       const SMDS_MeshNode* nPrev = *nIt;
8005       double bordLength = 0;
8006       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8007         const SMDS_MeshNode* nCur = *nIt;
8008         gp_XYZ segment (nCur->X() - nPrev->X(),
8009                         nCur->Y() - nPrev->Y(),
8010                         nCur->Z() - nPrev->Z());
8011         double segmentLen = segment.Modulus();
8012         bordLength += segmentLen;
8013         param[ iBord ][ iNode ] = bordLength;
8014         nPrev = nCur;
8015       }
8016       // normalize within [0,1]
8017       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8018         param[ iBord ][ iNode ] /= bordLength;
8019       }
8020     }
8021
8022     // loop on border segments
8023     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8024     int i[ 2 ] = { 0, 0 };
8025     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8026     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8027
8028     TElemOfNodeListMap insertMap;
8029     TElemOfNodeListMap::iterator insertMapIt;
8030     // insertMap is
8031     // key:   elem to insert nodes into
8032     // value: 2 nodes to insert between + nodes to be inserted
8033     do {
8034       bool next[ 2 ] = { false, false };
8035
8036       // find min adjacent segment length after sewing
8037       double nextParam = 10., prevParam = 0;
8038       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8039         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8040           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8041         if ( i[ iBord ] > 0 )
8042           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8043       }
8044       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8045       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8046       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8047
8048       // choose to insert or to merge nodes
8049       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8050       if ( Abs( du ) <= minSegLen * 0.2 ) {
8051         // merge
8052         // ------
8053         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8054         const SMDS_MeshNode* n0 = *nIt[0];
8055         const SMDS_MeshNode* n1 = *nIt[1];
8056         nodeGroupsToMerge.back().push_back( n1 );
8057         nodeGroupsToMerge.back().push_back( n0 );
8058         // position of node of the border changes due to merge
8059         param[ 0 ][ i[0] ] += du;
8060         // move n1 for the sake of elem shape evaluation during insertion.
8061         // n1 will be removed by MergeNodes() anyway
8062         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8063         next[0] = next[1] = true;
8064       }
8065       else {
8066         // insert
8067         // ------
8068         int intoBord = ( du < 0 ) ? 0 : 1;
8069         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8070         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8071         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8072         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8073         if ( intoBord == 1 ) {
8074           // move node of the border to be on a link of elem of the side
8075           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8076           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8077           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8078           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8079           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8080         }
8081         insertMapIt = insertMap.find( elem );
8082         bool notFound = ( insertMapIt == insertMap.end() );
8083         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8084         if ( otherLink ) {
8085           // insert into another link of the same element:
8086           // 1. perform insertion into the other link of the elem
8087           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8088           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8089           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8090           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8091           // 2. perform insertion into the link of adjacent faces
8092           while (true) {
8093             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8094             if ( adjElem )
8095               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8096             else
8097               break;
8098           }
8099           if (toCreatePolyedrs) {
8100             // perform insertion into the links of adjacent volumes
8101             UpdateVolumes(n12, n22, nodeList);
8102           }
8103           // 3. find an element appeared on n1 and n2 after the insertion
8104           insertMap.erase( elem );
8105           elem = findAdjacentFace( n1, n2, 0 );
8106         }
8107         if ( notFound || otherLink ) {
8108           // add element and nodes of the side into the insertMap
8109           insertMapIt = insertMap.insert
8110             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8111           (*insertMapIt).second.push_back( n1 );
8112           (*insertMapIt).second.push_back( n2 );
8113         }
8114         // add node to be inserted into elem
8115         (*insertMapIt).second.push_back( nIns );
8116         next[ 1 - intoBord ] = true;
8117       }
8118
8119       // go to the next segment
8120       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8121         if ( next[ iBord ] ) {
8122           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8123             eIt[ iBord ]++;
8124           nPrev[ iBord ] = *nIt[ iBord ];
8125           nIt[ iBord ]++; i[ iBord ]++;
8126         }
8127       }
8128     }
8129     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8130
8131     // perform insertion of nodes into elements
8132
8133     for (insertMapIt = insertMap.begin();
8134          insertMapIt != insertMap.end();
8135          insertMapIt++ )
8136     {
8137       const SMDS_MeshElement* elem = (*insertMapIt).first;
8138       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8139       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8140       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8141
8142       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8143
8144       if ( !theSideIsFreeBorder ) {
8145         // look for and insert nodes into the faces adjacent to elem
8146         while (true) {
8147           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8148           if ( adjElem )
8149             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8150           else
8151             break;
8152         }
8153       }
8154       if (toCreatePolyedrs) {
8155         // perform insertion into the links of adjacent volumes
8156         UpdateVolumes(n1, n2, nodeList);
8157       }
8158     }
8159
8160     delete param[0];
8161     delete param[1];
8162   } // end: insert new nodes
8163
8164   MergeNodes ( nodeGroupsToMerge );
8165
8166   return aResult;
8167 }
8168
8169 //=======================================================================
8170 //function : InsertNodesIntoLink
8171 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8172 //           and theBetweenNode2 and split theElement
8173 //=======================================================================
8174
8175 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8176                                            const SMDS_MeshNode*        theBetweenNode1,
8177                                            const SMDS_MeshNode*        theBetweenNode2,
8178                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8179                                            const bool                  toCreatePoly)
8180 {
8181   if ( theFace->GetType() != SMDSAbs_Face ) return;
8182
8183   // find indices of 2 link nodes and of the rest nodes
8184   int iNode = 0, il1, il2, i3, i4;
8185   il1 = il2 = i3 = i4 = -1;
8186   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8187   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8188
8189   if(theFace->IsQuadratic()) {
8190     const SMDS_VtkFace* F =
8191       dynamic_cast<const SMDS_VtkFace*>(theFace);
8192     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8193     // use special nodes iterator
8194     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8195     while( anIter->more() ) {
8196       const SMDS_MeshNode* n = cast2Node(anIter->next());
8197       if ( n == theBetweenNode1 )
8198         il1 = iNode;
8199       else if ( n == theBetweenNode2 )
8200         il2 = iNode;
8201       else if ( i3 < 0 )
8202         i3 = iNode;
8203       else
8204         i4 = iNode;
8205       nodes[ iNode++ ] = n;
8206     }
8207   }
8208   else {
8209     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8210     while ( nodeIt->more() ) {
8211       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8212       if ( n == theBetweenNode1 )
8213         il1 = iNode;
8214       else if ( n == theBetweenNode2 )
8215         il2 = iNode;
8216       else if ( i3 < 0 )
8217         i3 = iNode;
8218       else
8219         i4 = iNode;
8220       nodes[ iNode++ ] = n;
8221     }
8222   }
8223   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8224     return ;
8225
8226   // arrange link nodes to go one after another regarding the face orientation
8227   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8228   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8229   if ( reverse ) {
8230     iNode = il1;
8231     il1 = il2;
8232     il2 = iNode;
8233     aNodesToInsert.reverse();
8234   }
8235   // check that not link nodes of a quadrangles are in good order
8236   int nbFaceNodes = theFace->NbNodes();
8237   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8238     iNode = i3;
8239     i3 = i4;
8240     i4 = iNode;
8241   }
8242
8243   if (toCreatePoly || theFace->IsPoly()) {
8244
8245     iNode = 0;
8246     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8247
8248     // add nodes of face up to first node of link
8249     bool isFLN = false;
8250
8251     if(theFace->IsQuadratic()) {
8252       const SMDS_VtkFace* F =
8253         dynamic_cast<const SMDS_VtkFace*>(theFace);
8254       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8255       // use special nodes iterator
8256       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8257       while( anIter->more()  && !isFLN ) {
8258         const SMDS_MeshNode* n = cast2Node(anIter->next());
8259         poly_nodes[iNode++] = n;
8260         if (n == nodes[il1]) {
8261           isFLN = true;
8262         }
8263       }
8264       // add nodes to insert
8265       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8266       for (; nIt != aNodesToInsert.end(); nIt++) {
8267         poly_nodes[iNode++] = *nIt;
8268       }
8269       // add nodes of face starting from last node of link
8270       while ( anIter->more() ) {
8271         poly_nodes[iNode++] = cast2Node(anIter->next());
8272       }
8273     }
8274     else {
8275       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8276       while ( nodeIt->more() && !isFLN ) {
8277         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8278         poly_nodes[iNode++] = n;
8279         if (n == nodes[il1]) {
8280           isFLN = true;
8281         }
8282       }
8283       // add nodes to insert
8284       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8285       for (; nIt != aNodesToInsert.end(); nIt++) {
8286         poly_nodes[iNode++] = *nIt;
8287       }
8288       // add nodes of face starting from last node of link
8289       while ( nodeIt->more() ) {
8290         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8291         poly_nodes[iNode++] = n;
8292       }
8293     }
8294
8295     // edit or replace the face
8296     SMESHDS_Mesh *aMesh = GetMeshDS();
8297
8298     if (theFace->IsPoly()) {
8299       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8300     }
8301     else {
8302       int aShapeId = FindShape( theFace );
8303
8304       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8305       myLastCreatedElems.Append(newElem);
8306       if ( aShapeId && newElem )
8307         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8308
8309       aMesh->RemoveElement(theFace);
8310     }
8311     return;
8312   }
8313
8314   SMESHDS_Mesh *aMesh = GetMeshDS();
8315   if( !theFace->IsQuadratic() ) {
8316
8317     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8318     int nbLinkNodes = 2 + aNodesToInsert.size();
8319     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8320     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8321     linkNodes[ 0 ] = nodes[ il1 ];
8322     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8323     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8324     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8325       linkNodes[ iNode++ ] = *nIt;
8326     }
8327     // decide how to split a quadrangle: compare possible variants
8328     // and choose which of splits to be a quadrangle
8329     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8330     if ( nbFaceNodes == 3 ) {
8331       iBestQuad = nbSplits;
8332       i4 = i3;
8333     }
8334     else if ( nbFaceNodes == 4 ) {
8335       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8336       double aBestRate = DBL_MAX;
8337       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8338         i1 = 0; i2 = 1;
8339         double aBadRate = 0;
8340         // evaluate elements quality
8341         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8342           if ( iSplit == iQuad ) {
8343             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8344                                    linkNodes[ i2++ ],
8345                                    nodes[ i3 ],
8346                                    nodes[ i4 ]);
8347             aBadRate += getBadRate( &quad, aCrit );
8348           }
8349           else {
8350             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8351                                    linkNodes[ i2++ ],
8352                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8353             aBadRate += getBadRate( &tria, aCrit );
8354           }
8355         }
8356         // choice
8357         if ( aBadRate < aBestRate ) {
8358           iBestQuad = iQuad;
8359           aBestRate = aBadRate;
8360         }
8361       }
8362     }
8363
8364     // create new elements
8365     int aShapeId = FindShape( theFace );
8366
8367     i1 = 0; i2 = 1;
8368     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8369       SMDS_MeshElement* newElem = 0;
8370       if ( iSplit == iBestQuad )
8371         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8372                                   linkNodes[ i2++ ],
8373                                   nodes[ i3 ],
8374                                   nodes[ i4 ]);
8375       else
8376         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8377                                   linkNodes[ i2++ ],
8378                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8379       myLastCreatedElems.Append(newElem);
8380       if ( aShapeId && newElem )
8381         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8382     }
8383
8384     // change nodes of theFace
8385     const SMDS_MeshNode* newNodes[ 4 ];
8386     newNodes[ 0 ] = linkNodes[ i1 ];
8387     newNodes[ 1 ] = linkNodes[ i2 ];
8388     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8389     newNodes[ 3 ] = nodes[ i4 ];
8390     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8391     const SMDS_MeshElement* newElem = 0;
8392     if (iSplit == iBestQuad)
8393       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8394     else
8395       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8396     myLastCreatedElems.Append(newElem);
8397     if ( aShapeId && newElem )
8398       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8399 } // end if(!theFace->IsQuadratic())
8400   else { // theFace is quadratic
8401     // we have to split theFace on simple triangles and one simple quadrangle
8402     int tmp = il1/2;
8403     int nbshift = tmp*2;
8404     // shift nodes in nodes[] by nbshift
8405     int i,j;
8406     for(i=0; i<nbshift; i++) {
8407       const SMDS_MeshNode* n = nodes[0];
8408       for(j=0; j<nbFaceNodes-1; j++) {
8409         nodes[j] = nodes[j+1];
8410       }
8411       nodes[nbFaceNodes-1] = n;
8412     }
8413     il1 = il1 - nbshift;
8414     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8415     //   n0      n1     n2    n0      n1     n2
8416     //     +-----+-----+        +-----+-----+
8417     //      \         /         |           |
8418     //       \       /          |           |
8419     //      n5+     +n3       n7+           +n3
8420     //         \   /            |           |
8421     //          \ /             |           |
8422     //           +              +-----+-----+
8423     //           n4           n6      n5     n4
8424
8425     // create new elements
8426     int aShapeId = FindShape( theFace );
8427
8428     int n1,n2,n3;
8429     if(nbFaceNodes==6) { // quadratic triangle
8430       SMDS_MeshElement* newElem =
8431         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8432       myLastCreatedElems.Append(newElem);
8433       if ( aShapeId && newElem )
8434         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8435       if(theFace->IsMediumNode(nodes[il1])) {
8436         // create quadrangle
8437         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8438         myLastCreatedElems.Append(newElem);
8439         if ( aShapeId && newElem )
8440           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8441         n1 = 1;
8442         n2 = 2;
8443         n3 = 3;
8444       }
8445       else {
8446         // create quadrangle
8447         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8448         myLastCreatedElems.Append(newElem);
8449         if ( aShapeId && newElem )
8450           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8451         n1 = 0;
8452         n2 = 1;
8453         n3 = 5;
8454       }
8455     }
8456     else { // nbFaceNodes==8 - quadratic quadrangle
8457       SMDS_MeshElement* newElem =
8458         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8459       myLastCreatedElems.Append(newElem);
8460       if ( aShapeId && newElem )
8461         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8462       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8463       myLastCreatedElems.Append(newElem);
8464       if ( aShapeId && newElem )
8465         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8466       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8467       myLastCreatedElems.Append(newElem);
8468       if ( aShapeId && newElem )
8469         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8470       if(theFace->IsMediumNode(nodes[il1])) {
8471         // create quadrangle
8472         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8473         myLastCreatedElems.Append(newElem);
8474         if ( aShapeId && newElem )
8475           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8476         n1 = 1;
8477         n2 = 2;
8478         n3 = 3;
8479       }
8480       else {
8481         // create quadrangle
8482         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8483         myLastCreatedElems.Append(newElem);
8484         if ( aShapeId && newElem )
8485           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8486         n1 = 0;
8487         n2 = 1;
8488         n3 = 7;
8489       }
8490     }
8491     // create needed triangles using n1,n2,n3 and inserted nodes
8492     int nbn = 2 + aNodesToInsert.size();
8493     //const SMDS_MeshNode* aNodes[nbn];
8494     vector<const SMDS_MeshNode*> aNodes(nbn);
8495     aNodes[0] = nodes[n1];
8496     aNodes[nbn-1] = nodes[n2];
8497     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8498     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8499       aNodes[iNode++] = *nIt;
8500     }
8501     for(i=1; i<nbn; i++) {
8502       SMDS_MeshElement* newElem =
8503         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8504       myLastCreatedElems.Append(newElem);
8505       if ( aShapeId && newElem )
8506         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8507     }
8508   }
8509   // remove old face
8510   aMesh->RemoveElement(theFace);
8511 }
8512
8513 //=======================================================================
8514 //function : UpdateVolumes
8515 //purpose  :
8516 //=======================================================================
8517 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8518                                       const SMDS_MeshNode*        theBetweenNode2,
8519                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8520 {
8521   myLastCreatedElems.Clear();
8522   myLastCreatedNodes.Clear();
8523
8524   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8525   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8526     const SMDS_MeshElement* elem = invElemIt->next();
8527
8528     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8529     SMDS_VolumeTool aVolume (elem);
8530     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8531       continue;
8532
8533     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8534     int iface, nbFaces = aVolume.NbFaces();
8535     vector<const SMDS_MeshNode *> poly_nodes;
8536     vector<int> quantities (nbFaces);
8537
8538     for (iface = 0; iface < nbFaces; iface++) {
8539       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8540       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8541       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8542
8543       for (int inode = 0; inode < nbFaceNodes; inode++) {
8544         poly_nodes.push_back(faceNodes[inode]);
8545
8546         if (nbInserted == 0) {
8547           if (faceNodes[inode] == theBetweenNode1) {
8548             if (faceNodes[inode + 1] == theBetweenNode2) {
8549               nbInserted = theNodesToInsert.size();
8550
8551               // add nodes to insert
8552               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8553               for (; nIt != theNodesToInsert.end(); nIt++) {
8554                 poly_nodes.push_back(*nIt);
8555               }
8556             }
8557           }
8558           else if (faceNodes[inode] == theBetweenNode2) {
8559             if (faceNodes[inode + 1] == theBetweenNode1) {
8560               nbInserted = theNodesToInsert.size();
8561
8562               // add nodes to insert in reversed order
8563               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8564               nIt--;
8565               for (; nIt != theNodesToInsert.begin(); nIt--) {
8566                 poly_nodes.push_back(*nIt);
8567               }
8568               poly_nodes.push_back(*nIt);
8569             }
8570           }
8571           else {
8572           }
8573         }
8574       }
8575       quantities[iface] = nbFaceNodes + nbInserted;
8576     }
8577
8578     // Replace or update the volume
8579     SMESHDS_Mesh *aMesh = GetMeshDS();
8580
8581     if (elem->IsPoly()) {
8582       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8583
8584     }
8585     else {
8586       int aShapeId = FindShape( elem );
8587
8588       SMDS_MeshElement* newElem =
8589         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8590       myLastCreatedElems.Append(newElem);
8591       if (aShapeId && newElem)
8592         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8593
8594       aMesh->RemoveElement(elem);
8595     }
8596   }
8597 }
8598
8599 namespace
8600 {
8601   //================================================================================
8602   /*!
8603    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8604    */
8605   //================================================================================
8606
8607   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8608                            vector<const SMDS_MeshNode *> & nodes,
8609                            vector<int> &                   nbNodeInFaces )
8610   {
8611     nodes.clear();
8612     nbNodeInFaces.clear();
8613     SMDS_VolumeTool vTool ( elem );
8614     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8615     {
8616       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8617       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8618       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8619     }
8620   }
8621 }
8622
8623 //=======================================================================
8624 /*!
8625  * \brief Convert elements contained in a submesh to quadratic
8626  * \return int - nb of checked elements
8627  */
8628 //=======================================================================
8629
8630 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8631                                              SMESH_MesherHelper& theHelper,
8632                                              const bool          theForce3d)
8633 {
8634   int nbElem = 0;
8635   if( !theSm ) return nbElem;
8636
8637   vector<int> nbNodeInFaces;
8638   vector<const SMDS_MeshNode *> nodes;
8639   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8640   while(ElemItr->more())
8641   {
8642     nbElem++;
8643     const SMDS_MeshElement* elem = ElemItr->next();
8644     if( !elem ) continue;
8645
8646     // analyse a necessity of conversion
8647     const SMDSAbs_ElementType aType = elem->GetType();
8648     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8649       continue;
8650     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8651     bool hasCentralNodes = false;
8652     if ( elem->IsQuadratic() )
8653     {
8654       bool alreadyOK;
8655       switch ( aGeomType ) {
8656       case SMDSEntity_Quad_Triangle:
8657       case SMDSEntity_Quad_Quadrangle:
8658       case SMDSEntity_Quad_Hexa:
8659         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8660
8661       case SMDSEntity_BiQuad_Triangle:
8662       case SMDSEntity_BiQuad_Quadrangle:
8663       case SMDSEntity_TriQuad_Hexa:
8664         alreadyOK = theHelper.GetIsBiQuadratic();
8665         hasCentralNodes = true;
8666         break;
8667       default:
8668         alreadyOK = true;
8669       }
8670       // take into account already present modium nodes
8671       switch ( aType ) {
8672       case SMDSAbs_Volume:
8673         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8674       case SMDSAbs_Face:
8675         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8676       case SMDSAbs_Edge:
8677         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8678       default:;
8679       }
8680       if ( alreadyOK )
8681         continue;
8682     }
8683     // get elem data needed to re-create it
8684     //
8685     const int id      = elem->GetID();
8686     const int nbNodes = elem->NbCornerNodes();
8687     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8688     if ( aGeomType == SMDSEntity_Polyhedra )
8689       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8690     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8691       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8692
8693     // remove a linear element
8694     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8695
8696     // remove central nodes of biquadratic elements (biquad->quad convertion)
8697     if ( hasCentralNodes )
8698       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8699         if ( nodes[i]->NbInverseElements() == 0 )
8700           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8701
8702     const SMDS_MeshElement* NewElem = 0;
8703
8704     switch( aType )
8705     {
8706     case SMDSAbs_Edge :
8707       {
8708         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8709         break;
8710       }
8711     case SMDSAbs_Face :
8712       {
8713         switch(nbNodes)
8714         {
8715         case 3:
8716           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8717           break;
8718         case 4:
8719           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8720           break;
8721         default:
8722           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8723         }
8724         break;
8725       }
8726     case SMDSAbs_Volume :
8727       {
8728         switch( aGeomType )
8729         {
8730         case SMDSEntity_Tetra:
8731           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8732           break;
8733         case SMDSEntity_Pyramid:
8734           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8735           break;
8736         case SMDSEntity_Penta:
8737           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8738           break;
8739         case SMDSEntity_Hexa:
8740         case SMDSEntity_Quad_Hexa:
8741         case SMDSEntity_TriQuad_Hexa:
8742           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8743                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8744           break;
8745         case SMDSEntity_Hexagonal_Prism:
8746         default:
8747           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8748         }
8749         break;
8750       }
8751     default :
8752       continue;
8753     }
8754     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8755     if( NewElem && NewElem->getshapeId() < 1 )
8756       theSm->AddElement( NewElem );
8757   }
8758   return nbElem;
8759 }
8760 //=======================================================================
8761 //function : ConvertToQuadratic
8762 //purpose  :
8763 //=======================================================================
8764
8765 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8766 {
8767   SMESHDS_Mesh* meshDS = GetMeshDS();
8768
8769   SMESH_MesherHelper aHelper(*myMesh);
8770
8771   aHelper.SetIsQuadratic( true );
8772   aHelper.SetIsBiQuadratic( theToBiQuad );
8773   aHelper.SetElementsOnShape(true);
8774   aHelper.ToFixNodeParameters( true );
8775
8776   // convert elements assigned to sub-meshes
8777   int nbCheckedElems = 0;
8778   if ( myMesh->HasShapeToMesh() )
8779   {
8780     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8781     {
8782       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8783       while ( smIt->more() ) {
8784         SMESH_subMesh* sm = smIt->next();
8785         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8786           aHelper.SetSubShape( sm->GetSubShape() );
8787           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8788         }
8789       }
8790     }
8791   }
8792
8793   // convert elements NOT assigned to sub-meshes
8794   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8795   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8796   {
8797     aHelper.SetElementsOnShape(false);
8798     SMESHDS_SubMesh *smDS = 0;
8799
8800     // convert edges
8801     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8802     while( aEdgeItr->more() )
8803     {
8804       const SMDS_MeshEdge* edge = aEdgeItr->next();
8805       if ( !edge->IsQuadratic() )
8806       {
8807         int                  id = edge->GetID();
8808         const SMDS_MeshNode* n1 = edge->GetNode(0);
8809         const SMDS_MeshNode* n2 = edge->GetNode(1);
8810
8811         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8812
8813         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8814         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8815       }
8816       else
8817       {
8818         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8819       }
8820     }
8821
8822     // convert faces
8823     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8824     while( aFaceItr->more() )
8825     {
8826       const SMDS_MeshFace* face = aFaceItr->next();
8827       if ( !face ) continue;
8828       
8829       const SMDSAbs_EntityType type = face->GetEntityType();
8830       bool alreadyOK;
8831       switch( type )
8832       {
8833       case SMDSEntity_Quad_Triangle:
8834       case SMDSEntity_Quad_Quadrangle:
8835         alreadyOK = !theToBiQuad;
8836         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8837         break;
8838       case SMDSEntity_BiQuad_Triangle:
8839       case SMDSEntity_BiQuad_Quadrangle:
8840         alreadyOK = theToBiQuad;
8841         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8842         break;
8843       default: alreadyOK = false;
8844       }
8845       if ( alreadyOK )
8846         continue;
8847
8848       const int id = face->GetID();
8849       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8850
8851       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8852
8853       SMDS_MeshFace * NewFace = 0;
8854       switch( type )
8855       {
8856       case SMDSEntity_Triangle:
8857       case SMDSEntity_Quad_Triangle:
8858       case SMDSEntity_BiQuad_Triangle:
8859         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8860         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8861           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8862         break;
8863
8864       case SMDSEntity_Quadrangle:
8865       case SMDSEntity_Quad_Quadrangle:
8866       case SMDSEntity_BiQuad_Quadrangle:
8867         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8868         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8869           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8870         break;
8871
8872       default:;
8873         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8874       }
8875       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8876     }
8877
8878     // convert volumes
8879     vector<int> nbNodeInFaces;
8880     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8881     while(aVolumeItr->more())
8882     {
8883       const SMDS_MeshVolume* volume = aVolumeItr->next();
8884       if ( !volume ) continue;
8885
8886       const SMDSAbs_EntityType type = volume->GetEntityType();
8887       if ( volume->IsQuadratic() )
8888       {
8889         bool alreadyOK;
8890         switch ( type )
8891         {
8892         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8893         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8894         default:                      alreadyOK = true;
8895         }
8896         if ( alreadyOK )
8897         {
8898           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8899           continue;
8900         }
8901       }
8902       const int id = volume->GetID();
8903       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8904       if ( type == SMDSEntity_Polyhedra )
8905         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8906       else if ( type == SMDSEntity_Hexagonal_Prism )
8907         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8908
8909       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8910
8911       SMDS_MeshVolume * NewVolume = 0;
8912       switch ( type )
8913       {
8914       case SMDSEntity_Tetra:
8915         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8916         break;
8917       case SMDSEntity_Hexa:
8918       case SMDSEntity_Quad_Hexa:
8919       case SMDSEntity_TriQuad_Hexa:
8920         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8921                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8922         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8923           if ( nodes[i]->NbInverseElements() == 0 )
8924             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8925         break;
8926       case SMDSEntity_Pyramid:
8927         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8928                                       nodes[3], nodes[4], id, theForce3d);
8929         break;
8930       case SMDSEntity_Penta:
8931         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8932                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8933         break;
8934       case SMDSEntity_Hexagonal_Prism:
8935       default:
8936         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8937       }
8938       ReplaceElemInGroups(volume, NewVolume, meshDS);
8939     }
8940   }
8941
8942   if ( !theForce3d )
8943   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8944     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8945     // aHelper.FixQuadraticElements(myError);
8946     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8947   }
8948 }
8949
8950 //================================================================================
8951 /*!
8952  * \brief Makes given elements quadratic
8953  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8954  *  \param theElements - elements to make quadratic
8955  */
8956 //================================================================================
8957
8958 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8959                                           TIDSortedElemSet& theElements,
8960                                           const bool        theToBiQuad)
8961 {
8962   if ( theElements.empty() ) return;
8963
8964   // we believe that all theElements are of the same type
8965   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8966
8967   // get all nodes shared by theElements
8968   TIDSortedNodeSet allNodes;
8969   TIDSortedElemSet::iterator eIt = theElements.begin();
8970   for ( ; eIt != theElements.end(); ++eIt )
8971     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8972
8973   // complete theElements with elements of lower dim whose all nodes are in allNodes
8974
8975   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8976   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8977   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8978   for ( ; nIt != allNodes.end(); ++nIt )
8979   {
8980     const SMDS_MeshNode* n = *nIt;
8981     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8982     while ( invIt->more() )
8983     {
8984       const SMDS_MeshElement*      e = invIt->next();
8985       const SMDSAbs_ElementType type = e->GetType();
8986       if ( e->IsQuadratic() )
8987       {
8988         quadAdjacentElems[ type ].insert( e );
8989
8990         bool alreadyOK;
8991         switch ( e->GetEntityType() ) {
8992         case SMDSEntity_Quad_Triangle:
8993         case SMDSEntity_Quad_Quadrangle:
8994         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8995         case SMDSEntity_BiQuad_Triangle:
8996         case SMDSEntity_BiQuad_Quadrangle:
8997         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8998         default:                           alreadyOK = true;
8999         }
9000         if ( alreadyOK )
9001           continue;
9002       }
9003       if ( type >= elemType )
9004         continue; // same type or more complex linear element
9005
9006       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9007         continue; // e is already checked
9008
9009       // check nodes
9010       bool allIn = true;
9011       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9012       while ( nodeIt->more() && allIn )
9013         allIn = allNodes.count( nodeIt->next() );
9014       if ( allIn )
9015         theElements.insert(e );
9016     }
9017   }
9018
9019   SMESH_MesherHelper helper(*myMesh);
9020   helper.SetIsQuadratic( true );
9021   helper.SetIsBiQuadratic( theToBiQuad );
9022
9023   // add links of quadratic adjacent elements to the helper
9024
9025   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9026     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9027           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9028     {
9029       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9030     }
9031   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9032     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9033           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9034     {
9035       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9036     }
9037   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9038     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9039           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9040     {
9041       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9042     }
9043
9044   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9045
9046   SMESHDS_Mesh*  meshDS = GetMeshDS();
9047   SMESHDS_SubMesh* smDS = 0;
9048   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9049   {
9050     const SMDS_MeshElement* elem = *eIt;
9051
9052     bool alreadyOK;
9053     int nbCentralNodes = 0;
9054     switch ( elem->GetEntityType() ) {
9055       // linear convertible
9056     case SMDSEntity_Edge:
9057     case SMDSEntity_Triangle:
9058     case SMDSEntity_Quadrangle:
9059     case SMDSEntity_Tetra:
9060     case SMDSEntity_Pyramid:
9061     case SMDSEntity_Hexa:
9062     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9063       // quadratic that can become bi-quadratic
9064     case SMDSEntity_Quad_Triangle:
9065     case SMDSEntity_Quad_Quadrangle:
9066     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9067       // bi-quadratic
9068     case SMDSEntity_BiQuad_Triangle:
9069     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9070     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9071       // the rest
9072     default:                           alreadyOK = true;
9073     }
9074     if ( alreadyOK ) continue;
9075
9076     const SMDSAbs_ElementType type = elem->GetType();
9077     const int                   id = elem->GetID();
9078     const int              nbNodes = elem->NbCornerNodes();
9079     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9080
9081     helper.SetSubShape( elem->getshapeId() );
9082
9083     if ( !smDS || !smDS->Contains( elem ))
9084       smDS = meshDS->MeshElements( elem->getshapeId() );
9085     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9086
9087     SMDS_MeshElement * newElem = 0;
9088     switch( nbNodes )
9089     {
9090     case 4: // cases for most frequently used element types go first (for optimization)
9091       if ( type == SMDSAbs_Volume )
9092         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9093       else
9094         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9095       break;
9096     case 8:
9097       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9098                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9099       break;
9100     case 3:
9101       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9102       break;
9103     case 2:
9104       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9105       break;
9106     case 5:
9107       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9108                                  nodes[4], id, theForce3d);
9109       break;
9110     case 6:
9111       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112                                  nodes[4], nodes[5], id, theForce3d);
9113       break;
9114     default:;
9115     }
9116     ReplaceElemInGroups( elem, newElem, meshDS);
9117     if( newElem && smDS )
9118       smDS->AddElement( newElem );
9119
9120      // remove central nodes
9121     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9122       if ( nodes[i]->NbInverseElements() == 0 )
9123         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9124
9125   } // loop on theElements
9126
9127   if ( !theForce3d )
9128   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9129     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9130     // helper.FixQuadraticElements( myError );
9131     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9132   }
9133 }
9134
9135 //=======================================================================
9136 /*!
9137  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9138  * \return int - nb of checked elements
9139  */
9140 //=======================================================================
9141
9142 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9143                                      SMDS_ElemIteratorPtr theItr,
9144                                      const int            theShapeID)
9145 {
9146   int nbElem = 0;
9147   SMESHDS_Mesh* meshDS = GetMeshDS();
9148
9149   while( theItr->more() )
9150   {
9151     const SMDS_MeshElement* elem = theItr->next();
9152     nbElem++;
9153     if( elem && elem->IsQuadratic())
9154     {
9155       int id                    = elem->GetID();
9156       int nbCornerNodes         = elem->NbCornerNodes();
9157       SMDSAbs_ElementType aType = elem->GetType();
9158
9159       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9160
9161       //remove a quadratic element
9162       if ( !theSm || !theSm->Contains( elem ))
9163         theSm = meshDS->MeshElements( elem->getshapeId() );
9164       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9165
9166       // remove medium nodes
9167       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9168         if ( nodes[i]->NbInverseElements() == 0 )
9169           meshDS->RemoveFreeNode( nodes[i], theSm );
9170
9171       // add a linear element
9172       nodes.resize( nbCornerNodes );
9173       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9174       ReplaceElemInGroups(elem, newElem, meshDS);
9175       if( theSm && newElem )
9176         theSm->AddElement( newElem );
9177     }
9178   }
9179   return nbElem;
9180 }
9181
9182 //=======================================================================
9183 //function : ConvertFromQuadratic
9184 //purpose  :
9185 //=======================================================================
9186
9187 bool SMESH_MeshEditor::ConvertFromQuadratic()
9188 {
9189   int nbCheckedElems = 0;
9190   if ( myMesh->HasShapeToMesh() )
9191   {
9192     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9193     {
9194       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9195       while ( smIt->more() ) {
9196         SMESH_subMesh* sm = smIt->next();
9197         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9198           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9199       }
9200     }
9201   }
9202
9203   int totalNbElems =
9204     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9205   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9206   {
9207     SMESHDS_SubMesh *aSM = 0;
9208     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9209   }
9210
9211   return true;
9212 }
9213
9214 namespace
9215 {
9216   //================================================================================
9217   /*!
9218    * \brief Return true if all medium nodes of the element are in the node set
9219    */
9220   //================================================================================
9221
9222   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9223   {
9224     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9225       if ( !nodeSet.count( elem->GetNode(i) ))
9226         return false;
9227     return true;
9228   }
9229 }
9230
9231 //================================================================================
9232 /*!
9233  * \brief Makes given elements linear
9234  */
9235 //================================================================================
9236
9237 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9238 {
9239   if ( theElements.empty() ) return;
9240
9241   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9242   set<int> mediumNodeIDs;
9243   TIDSortedElemSet::iterator eIt = theElements.begin();
9244   for ( ; eIt != theElements.end(); ++eIt )
9245   {
9246     const SMDS_MeshElement* e = *eIt;
9247     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9248       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9249   }
9250
9251   // replace given elements by linear ones
9252   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9253   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9254
9255   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9256   // except those elements sharing medium nodes of quadratic element whose medium nodes
9257   // are not all in mediumNodeIDs
9258
9259   // get remaining medium nodes
9260   TIDSortedNodeSet mediumNodes;
9261   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9262   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9263     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9264       mediumNodes.insert( mediumNodes.end(), n );
9265
9266   // find more quadratic elements to convert
9267   TIDSortedElemSet moreElemsToConvert;
9268   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9269   for ( ; nIt != mediumNodes.end(); ++nIt )
9270   {
9271     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9272     while ( invIt->more() )
9273     {
9274       const SMDS_MeshElement* e = invIt->next();
9275       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9276       {
9277         // find a more complex element including e and
9278         // whose medium nodes are not in mediumNodes
9279         bool complexFound = false;
9280         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9281         {
9282           SMDS_ElemIteratorPtr invIt2 =
9283             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9284           while ( invIt2->more() )
9285           {
9286             const SMDS_MeshElement* eComplex = invIt2->next();
9287             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9288             {
9289               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9290               if ( nbCommonNodes == e->NbNodes())
9291               {
9292                 complexFound = true;
9293                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9294                 break;
9295               }
9296             }
9297           }
9298         }
9299         if ( !complexFound )
9300           moreElemsToConvert.insert( e );
9301       }
9302     }
9303   }
9304   elemIt = elemSetIterator( moreElemsToConvert );
9305   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9306 }
9307
9308 //=======================================================================
9309 //function : SewSideElements
9310 //purpose  :
9311 //=======================================================================
9312
9313 SMESH_MeshEditor::Sew_Error
9314 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9315                                    TIDSortedElemSet&    theSide2,
9316                                    const SMDS_MeshNode* theFirstNode1,
9317                                    const SMDS_MeshNode* theFirstNode2,
9318                                    const SMDS_MeshNode* theSecondNode1,
9319                                    const SMDS_MeshNode* theSecondNode2)
9320 {
9321   myLastCreatedElems.Clear();
9322   myLastCreatedNodes.Clear();
9323
9324   MESSAGE ("::::SewSideElements()");
9325   if ( theSide1.size() != theSide2.size() )
9326     return SEW_DIFF_NB_OF_ELEMENTS;
9327
9328   Sew_Error aResult = SEW_OK;
9329   // Algo:
9330   // 1. Build set of faces representing each side
9331   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9332   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9333
9334   // =======================================================================
9335   // 1. Build set of faces representing each side:
9336   // =======================================================================
9337   // a. build set of nodes belonging to faces
9338   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9339   // c. create temporary faces representing side of volumes if correspondent
9340   //    face does not exist
9341
9342   SMESHDS_Mesh* aMesh = GetMeshDS();
9343   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9344   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9345   TIDSortedElemSet             faceSet1, faceSet2;
9346   set<const SMDS_MeshElement*> volSet1,  volSet2;
9347   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9348   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9349   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9350   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9351   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9352   int iSide, iFace, iNode;
9353
9354   list<const SMDS_MeshElement* > tempFaceList;
9355   for ( iSide = 0; iSide < 2; iSide++ ) {
9356     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9357     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9358     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9359     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9360     set<const SMDS_MeshElement*>::iterator vIt;
9361     TIDSortedElemSet::iterator eIt;
9362     set<const SMDS_MeshNode*>::iterator    nIt;
9363
9364     // check that given nodes belong to given elements
9365     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9366     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9367     int firstIndex = -1, secondIndex = -1;
9368     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9369       const SMDS_MeshElement* elem = *eIt;
9370       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9371       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9372       if ( firstIndex > -1 && secondIndex > -1 ) break;
9373     }
9374     if ( firstIndex < 0 || secondIndex < 0 ) {
9375       // we can simply return until temporary faces created
9376       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9377     }
9378
9379     // -----------------------------------------------------------
9380     // 1a. Collect nodes of existing faces
9381     //     and build set of face nodes in order to detect missing
9382     //     faces corresponding to sides of volumes
9383     // -----------------------------------------------------------
9384
9385     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9386
9387     // loop on the given element of a side
9388     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9389       //const SMDS_MeshElement* elem = *eIt;
9390       const SMDS_MeshElement* elem = *eIt;
9391       if ( elem->GetType() == SMDSAbs_Face ) {
9392         faceSet->insert( elem );
9393         set <const SMDS_MeshNode*> faceNodeSet;
9394         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9395         while ( nodeIt->more() ) {
9396           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9397           nodeSet->insert( n );
9398           faceNodeSet.insert( n );
9399         }
9400         setOfFaceNodeSet.insert( faceNodeSet );
9401       }
9402       else if ( elem->GetType() == SMDSAbs_Volume )
9403         volSet->insert( elem );
9404     }
9405     // ------------------------------------------------------------------------------
9406     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9407     // ------------------------------------------------------------------------------
9408
9409     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9410       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9411       while ( fIt->more() ) { // loop on faces sharing a node
9412         const SMDS_MeshElement* f = fIt->next();
9413         if ( faceSet->find( f ) == faceSet->end() ) {
9414           // check if all nodes are in nodeSet and
9415           // complete setOfFaceNodeSet if they are
9416           set <const SMDS_MeshNode*> faceNodeSet;
9417           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9418           bool allInSet = true;
9419           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9420             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9421             if ( nodeSet->find( n ) == nodeSet->end() )
9422               allInSet = false;
9423             else
9424               faceNodeSet.insert( n );
9425           }
9426           if ( allInSet ) {
9427             faceSet->insert( f );
9428             setOfFaceNodeSet.insert( faceNodeSet );
9429           }
9430         }
9431       }
9432     }
9433
9434     // -------------------------------------------------------------------------
9435     // 1c. Create temporary faces representing sides of volumes if correspondent
9436     //     face does not exist
9437     // -------------------------------------------------------------------------
9438
9439     if ( !volSet->empty() ) {
9440       //int nodeSetSize = nodeSet->size();
9441
9442       // loop on given volumes
9443       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9444         SMDS_VolumeTool vol (*vIt);
9445         // loop on volume faces: find free faces
9446         // --------------------------------------
9447         list<const SMDS_MeshElement* > freeFaceList;
9448         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9449           if ( !vol.IsFreeFace( iFace ))
9450             continue;
9451           // check if there is already a face with same nodes in a face set
9452           const SMDS_MeshElement* aFreeFace = 0;
9453           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9454           int nbNodes = vol.NbFaceNodes( iFace );
9455           set <const SMDS_MeshNode*> faceNodeSet;
9456           vol.GetFaceNodes( iFace, faceNodeSet );
9457           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9458           if ( isNewFace ) {
9459             // no such a face is given but it still can exist, check it
9460             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9461             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9462           }
9463           if ( !aFreeFace ) {
9464             // create a temporary face
9465             if ( nbNodes == 3 ) {
9466               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9467               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9468             }
9469             else if ( nbNodes == 4 ) {
9470               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9471               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9472             }
9473             else {
9474               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9475               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9476               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9477             }
9478             if ( aFreeFace )
9479               tempFaceList.push_back( aFreeFace );
9480           }
9481
9482           if ( aFreeFace )
9483             freeFaceList.push_back( aFreeFace );
9484
9485         } // loop on faces of a volume
9486
9487         // choose one of several free faces of a volume
9488         // --------------------------------------------
9489         if ( freeFaceList.size() > 1 ) {
9490           // choose a face having max nb of nodes shared by other elems of a side
9491           int maxNbNodes = -1;
9492           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9493           while ( fIt != freeFaceList.end() ) { // loop on free faces
9494             int nbSharedNodes = 0;
9495             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9496             while ( nodeIt->more() ) { // loop on free face nodes
9497               const SMDS_MeshNode* n =
9498                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9499               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9500               while ( invElemIt->more() ) {
9501                 const SMDS_MeshElement* e = invElemIt->next();
9502                 nbSharedNodes += faceSet->count( e );
9503                 nbSharedNodes += elemSet->count( e );
9504               }
9505             }
9506             if ( nbSharedNodes > maxNbNodes ) {
9507               maxNbNodes = nbSharedNodes;
9508               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9509             }
9510             else if ( nbSharedNodes == maxNbNodes ) {
9511               fIt++;
9512             }
9513             else {
9514               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9515             }
9516           }
9517           if ( freeFaceList.size() > 1 )
9518           {
9519             // could not choose one face, use another way
9520             // choose a face most close to the bary center of the opposite side
9521             gp_XYZ aBC( 0., 0., 0. );
9522             set <const SMDS_MeshNode*> addedNodes;
9523             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9524             eIt = elemSet2->begin();
9525             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9526               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9527               while ( nodeIt->more() ) { // loop on free face nodes
9528                 const SMDS_MeshNode* n =
9529                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9530                 if ( addedNodes.insert( n ).second )
9531                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9532               }
9533             }
9534             aBC /= addedNodes.size();
9535             double minDist = DBL_MAX;
9536             fIt = freeFaceList.begin();
9537             while ( fIt != freeFaceList.end() ) { // loop on free faces
9538               double dist = 0;
9539               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9540               while ( nodeIt->more() ) { // loop on free face nodes
9541                 const SMDS_MeshNode* n =
9542                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9543                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9544                 dist += ( aBC - p ).SquareModulus();
9545               }
9546               if ( dist < minDist ) {
9547                 minDist = dist;
9548                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9549               }
9550               else
9551                 fIt = freeFaceList.erase( fIt++ );
9552             }
9553           }
9554         } // choose one of several free faces of a volume
9555
9556         if ( freeFaceList.size() == 1 ) {
9557           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9558           faceSet->insert( aFreeFace );
9559           // complete a node set with nodes of a found free face
9560           //           for ( iNode = 0; iNode < ; iNode++ )
9561           //             nodeSet->insert( fNodes[ iNode ] );
9562         }
9563
9564       } // loop on volumes of a side
9565
9566       //       // complete a set of faces if new nodes in a nodeSet appeared
9567       //       // ----------------------------------------------------------
9568       //       if ( nodeSetSize != nodeSet->size() ) {
9569       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9570       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9571       //           while ( fIt->more() ) { // loop on faces sharing a node
9572       //             const SMDS_MeshElement* f = fIt->next();
9573       //             if ( faceSet->find( f ) == faceSet->end() ) {
9574       //               // check if all nodes are in nodeSet and
9575       //               // complete setOfFaceNodeSet if they are
9576       //               set <const SMDS_MeshNode*> faceNodeSet;
9577       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9578       //               bool allInSet = true;
9579       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9580       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9581       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9582       //                   allInSet = false;
9583       //                 else
9584       //                   faceNodeSet.insert( n );
9585       //               }
9586       //               if ( allInSet ) {
9587       //                 faceSet->insert( f );
9588       //                 setOfFaceNodeSet.insert( faceNodeSet );
9589       //               }
9590       //             }
9591       //           }
9592       //         }
9593       //       }
9594     } // Create temporary faces, if there are volumes given
9595   } // loop on sides
9596
9597   if ( faceSet1.size() != faceSet2.size() ) {
9598     // delete temporary faces: they are in reverseElements of actual nodes
9599 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9600 //    while ( tmpFaceIt->more() )
9601 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9602 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9603 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9604 //      aMesh->RemoveElement(*tmpFaceIt);
9605     MESSAGE("Diff nb of faces");
9606     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9607   }
9608
9609   // ============================================================
9610   // 2. Find nodes to merge:
9611   //              bind a node to remove to a node to put instead
9612   // ============================================================
9613
9614   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9615   if ( theFirstNode1 != theFirstNode2 )
9616     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9617   if ( theSecondNode1 != theSecondNode2 )
9618     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9619
9620   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9621   set< long > linkIdSet; // links to process
9622   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9623
9624   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9625   list< NLink > linkList[2];
9626   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9627   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9628   // loop on links in linkList; find faces by links and append links
9629   // of the found faces to linkList
9630   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9631   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9632   {
9633     NLink link[] = { *linkIt[0], *linkIt[1] };
9634     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9635     if ( !linkIdSet.count( linkID ) )
9636       continue;
9637
9638     // by links, find faces in the face sets,
9639     // and find indices of link nodes in the found faces;
9640     // in a face set, there is only one or no face sharing a link
9641     // ---------------------------------------------------------------
9642
9643     const SMDS_MeshElement* face[] = { 0, 0 };
9644     vector<const SMDS_MeshNode*> fnodes[2];
9645     int iLinkNode[2][2];
9646     TIDSortedElemSet avoidSet;
9647     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9648       const SMDS_MeshNode* n1 = link[iSide].first;
9649       const SMDS_MeshNode* n2 = link[iSide].second;
9650       //cout << "Side " << iSide << " ";
9651       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9652       // find a face by two link nodes
9653       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9654                                                       *faceSetPtr[ iSide ], avoidSet,
9655                                                       &iLinkNode[iSide][0],
9656                                                       &iLinkNode[iSide][1] );
9657       if ( face[ iSide ])
9658       {
9659         //cout << " F " << face[ iSide]->GetID() <<endl;
9660         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9661         // put face nodes to fnodes
9662         if ( face[ iSide ]->IsQuadratic() )
9663         {
9664           // use interlaced nodes iterator
9665           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9666           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9667           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9668           while ( nIter->more() )
9669             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9670         }
9671         else
9672         {
9673           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9674                                   face[ iSide ]->end_nodes() );
9675         }
9676         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9677       }
9678     }
9679
9680     // check similarity of elements of the sides
9681     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9682       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9683       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9684         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9685       }
9686       else {
9687         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9688       }
9689       break; // do not return because it's necessary to remove tmp faces
9690     }
9691
9692     // set nodes to merge
9693     // -------------------
9694
9695     if ( face[0] && face[1] )  {
9696       const int nbNodes = face[0]->NbNodes();
9697       if ( nbNodes != face[1]->NbNodes() ) {
9698         MESSAGE("Diff nb of face nodes");
9699         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9700         break; // do not return because it s necessary to remove tmp faces
9701       }
9702       bool reverse[] = { false, false }; // order of nodes in the link
9703       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9704         // analyse link orientation in faces
9705         int i1 = iLinkNode[ iSide ][ 0 ];
9706         int i2 = iLinkNode[ iSide ][ 1 ];
9707         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9708       }
9709       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9710       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9711       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9712       {
9713         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9714                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9715       }
9716
9717       // add other links of the faces to linkList
9718       // -----------------------------------------
9719
9720       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9721         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9722         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9723         if ( !iter_isnew.second ) { // already in a set: no need to process
9724           linkIdSet.erase( iter_isnew.first );
9725         }
9726         else // new in set == encountered for the first time: add
9727         {
9728           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9729           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9730           linkList[0].push_back ( NLink( n1, n2 ));
9731           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9732         }
9733       }
9734     } // 2 faces found
9735
9736     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9737       break;
9738
9739   } // loop on link lists
9740
9741   if ( aResult == SEW_OK &&
9742        ( //linkIt[0] != linkList[0].end() ||
9743          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9744     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9745              " " << (faceSetPtr[1]->empty()));
9746     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9747   }
9748
9749   // ====================================================================
9750   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9751   // ====================================================================
9752
9753   // delete temporary faces
9754 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9755 //  while ( tmpFaceIt->more() )
9756 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9757   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9758   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9759     aMesh->RemoveElement(*tmpFaceIt);
9760
9761   if ( aResult != SEW_OK)
9762     return aResult;
9763
9764   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9765   // loop on nodes replacement map
9766   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9767   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9768     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9769       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9770       nodeIDsToRemove.push_back( nToRemove->GetID() );
9771       // loop on elements sharing nToRemove
9772       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9773       while ( invElemIt->more() ) {
9774         const SMDS_MeshElement* e = invElemIt->next();
9775         // get a new suite of nodes: make replacement
9776         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9777         vector< const SMDS_MeshNode*> nodes( nbNodes );
9778         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9779         while ( nIt->more() ) {
9780           const SMDS_MeshNode* n =
9781             static_cast<const SMDS_MeshNode*>( nIt->next() );
9782           nnIt = nReplaceMap.find( n );
9783           if ( nnIt != nReplaceMap.end() ) {
9784             nbReplaced++;
9785             n = (*nnIt).second;
9786           }
9787           nodes[ i++ ] = n;
9788         }
9789         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9790         //         elemIDsToRemove.push_back( e->GetID() );
9791         //       else
9792         if ( nbReplaced )
9793           {
9794             SMDSAbs_ElementType etyp = e->GetType();
9795             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9796             if (newElem)
9797               {
9798                 myLastCreatedElems.Append(newElem);
9799                 AddToSameGroups(newElem, e, aMesh);
9800                 int aShapeId = e->getshapeId();
9801                 if ( aShapeId )
9802                   {
9803                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9804                   }
9805               }
9806             aMesh->RemoveElement(e);
9807           }
9808       }
9809     }
9810
9811   Remove( nodeIDsToRemove, true );
9812
9813   return aResult;
9814 }
9815
9816 //================================================================================
9817 /*!
9818  * \brief Find corresponding nodes in two sets of faces
9819  * \param theSide1 - first face set
9820  * \param theSide2 - second first face
9821  * \param theFirstNode1 - a boundary node of set 1
9822  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9823  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9824  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9825  * \param nReplaceMap - output map of corresponding nodes
9826  * \return bool  - is a success or not
9827  */
9828 //================================================================================
9829
9830 #ifdef _DEBUG_
9831 //#define DEBUG_MATCHING_NODES
9832 #endif
9833
9834 SMESH_MeshEditor::Sew_Error
9835 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9836                                     set<const SMDS_MeshElement*>& theSide2,
9837                                     const SMDS_MeshNode*          theFirstNode1,
9838                                     const SMDS_MeshNode*          theFirstNode2,
9839                                     const SMDS_MeshNode*          theSecondNode1,
9840                                     const SMDS_MeshNode*          theSecondNode2,
9841                                     TNodeNodeMap &                nReplaceMap)
9842 {
9843   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9844
9845   nReplaceMap.clear();
9846   if ( theFirstNode1 != theFirstNode2 )
9847     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9848   if ( theSecondNode1 != theSecondNode2 )
9849     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9850
9851   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9852   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9853
9854   list< NLink > linkList[2];
9855   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9856   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9857
9858   // loop on links in linkList; find faces by links and append links
9859   // of the found faces to linkList
9860   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9861   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9862     NLink link[] = { *linkIt[0], *linkIt[1] };
9863     if ( linkSet.find( link[0] ) == linkSet.end() )
9864       continue;
9865
9866     // by links, find faces in the face sets,
9867     // and find indices of link nodes in the found faces;
9868     // in a face set, there is only one or no face sharing a link
9869     // ---------------------------------------------------------------
9870
9871     const SMDS_MeshElement* face[] = { 0, 0 };
9872     list<const SMDS_MeshNode*> notLinkNodes[2];
9873     //bool reverse[] = { false, false }; // order of notLinkNodes
9874     int nbNodes[2];
9875     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9876     {
9877       const SMDS_MeshNode* n1 = link[iSide].first;
9878       const SMDS_MeshNode* n2 = link[iSide].second;
9879       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9880       set< const SMDS_MeshElement* > facesOfNode1;
9881       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9882       {
9883         // during a loop of the first node, we find all faces around n1,
9884         // during a loop of the second node, we find one face sharing both n1 and n2
9885         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9886         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9887         while ( fIt->more() ) { // loop on faces sharing a node
9888           const SMDS_MeshElement* f = fIt->next();
9889           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9890               ! facesOfNode1.insert( f ).second ) // f encounters twice
9891           {
9892             if ( face[ iSide ] ) {
9893               MESSAGE( "2 faces per link " );
9894               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9895             }
9896             face[ iSide ] = f;
9897             faceSet->erase( f );
9898
9899             // get not link nodes
9900             int nbN = f->NbNodes();
9901             if ( f->IsQuadratic() )
9902               nbN /= 2;
9903             nbNodes[ iSide ] = nbN;
9904             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9905             int i1 = f->GetNodeIndex( n1 );
9906             int i2 = f->GetNodeIndex( n2 );
9907             int iEnd = nbN, iBeg = -1, iDelta = 1;
9908             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9909             if ( reverse ) {
9910               std::swap( iEnd, iBeg ); iDelta = -1;
9911             }
9912             int i = i2;
9913             while ( true ) {
9914               i += iDelta;
9915               if ( i == iEnd ) i = iBeg + iDelta;
9916               if ( i == i1 ) break;
9917               nodes.push_back ( f->GetNode( i ) );
9918             }
9919           }
9920         }
9921       }
9922     }
9923     // check similarity of elements of the sides
9924     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9925       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9926       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9927         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9928       }
9929       else {
9930         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9931       }
9932     }
9933
9934     // set nodes to merge
9935     // -------------------
9936
9937     if ( face[0] && face[1] )  {
9938       if ( nbNodes[0] != nbNodes[1] ) {
9939         MESSAGE("Diff nb of face nodes");
9940         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9941       }
9942 #ifdef DEBUG_MATCHING_NODES
9943       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9944                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9945                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9946 #endif
9947       int nbN = nbNodes[0];
9948       {
9949         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9950         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9951         for ( int i = 0 ; i < nbN - 2; ++i ) {
9952 #ifdef DEBUG_MATCHING_NODES
9953           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9954 #endif
9955           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9956         }
9957       }
9958
9959       // add other links of the face 1 to linkList
9960       // -----------------------------------------
9961
9962       const SMDS_MeshElement* f0 = face[0];
9963       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9964       for ( int i = 0; i < nbN; i++ )
9965       {
9966         const SMDS_MeshNode* n2 = f0->GetNode( i );
9967         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9968           linkSet.insert( SMESH_TLink( n1, n2 ));
9969         if ( !iter_isnew.second ) { // already in a set: no need to process
9970           linkSet.erase( iter_isnew.first );
9971         }
9972         else // new in set == encountered for the first time: add
9973         {
9974 #ifdef DEBUG_MATCHING_NODES
9975           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9976                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9977 #endif
9978           linkList[0].push_back ( NLink( n1, n2 ));
9979           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9980         }
9981         n1 = n2;
9982       }
9983     } // 2 faces found
9984   } // loop on link lists
9985
9986   return SEW_OK;
9987 }
9988
9989 //================================================================================
9990 /*!
9991  * \brief Create elements equal (on same nodes) to given ones
9992  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9993  *              elements of the uppest dimension are duplicated.
9994  */
9995 //================================================================================
9996
9997 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9998 {
9999   CrearLastCreated();
10000   SMESHDS_Mesh* mesh = GetMeshDS();
10001
10002   // get an element type and an iterator over elements
10003
10004   SMDSAbs_ElementType type;
10005   SMDS_ElemIteratorPtr elemIt;
10006   vector< const SMDS_MeshElement* > allElems;
10007   if ( theElements.empty() )
10008   {
10009     if ( mesh->NbNodes() == 0 )
10010       return;
10011     // get most complex type
10012     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10013       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10014       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10015     };
10016     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10017       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10018       {
10019         type = types[i];
10020         break;
10021       }
10022     // put all elements in the vector <allElems>
10023     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10024     elemIt = mesh->elementsIterator( type );
10025     while ( elemIt->more() )
10026       allElems.push_back( elemIt->next());
10027     elemIt = elemSetIterator( allElems );
10028   }
10029   else
10030   {
10031     type = (*theElements.begin())->GetType();
10032     elemIt = elemSetIterator( theElements );
10033   }
10034
10035   // duplicate elements
10036
10037   if ( type == SMDSAbs_Ball )
10038   {
10039     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10040     while ( elemIt->more() )
10041     {
10042       const SMDS_MeshElement* elem = elemIt->next();
10043       if ( elem->GetType() != SMDSAbs_Ball )
10044         continue;
10045       if (( elem = mesh->AddBall( elem->GetNode(0),
10046                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10047         myLastCreatedElems.Append( elem );
10048     }
10049   }
10050   else
10051   {
10052     vector< const SMDS_MeshNode* > nodes;
10053     while ( elemIt->more() )
10054     {
10055       const SMDS_MeshElement* elem = elemIt->next();
10056       if ( elem->GetType() != type )
10057         continue;
10058
10059       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10060
10061       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10062       {
10063         std::vector<int> quantities =
10064           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10065         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10066       }
10067       else
10068       {
10069         AddElement( nodes, type, elem->IsPoly() );
10070         elem = 0; // myLastCreatedElems is already filled
10071       }
10072       if ( elem )
10073         myLastCreatedElems.Append( elem );
10074     }
10075   }
10076 }
10077
10078 //================================================================================
10079 /*!
10080   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10081   \param theElems - the list of elements (edges or faces) to be replicated
10082   The nodes for duplication could be found from these elements
10083   \param theNodesNot - list of nodes to NOT replicate
10084   \param theAffectedElems - the list of elements (cells and edges) to which the
10085   replicated nodes should be associated to.
10086   \return TRUE if operation has been completed successfully, FALSE otherwise
10087 */
10088 //================================================================================
10089
10090 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10091                                     const TIDSortedElemSet& theNodesNot,
10092                                     const TIDSortedElemSet& theAffectedElems )
10093 {
10094   myLastCreatedElems.Clear();
10095   myLastCreatedNodes.Clear();
10096
10097   if ( theElems.size() == 0 )
10098     return false;
10099
10100   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10101   if ( !aMeshDS )
10102     return false;
10103
10104   bool res = false;
10105   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10106   // duplicate elements and nodes
10107   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10108   // replce nodes by duplications
10109   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10110   return res;
10111 }
10112
10113 //================================================================================
10114 /*!
10115   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10116   \param theMeshDS - mesh instance
10117   \param theElems - the elements replicated or modified (nodes should be changed)
10118   \param theNodesNot - nodes to NOT replicate
10119   \param theNodeNodeMap - relation of old node to new created node
10120   \param theIsDoubleElem - flag os to replicate element or modify
10121   \return TRUE if operation has been completed successfully, FALSE otherwise
10122 */
10123 //================================================================================
10124
10125 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10126                                     const TIDSortedElemSet& theElems,
10127                                     const TIDSortedElemSet& theNodesNot,
10128                                     std::map< const SMDS_MeshNode*,
10129                                     const SMDS_MeshNode* >& theNodeNodeMap,
10130                                     const bool theIsDoubleElem )
10131 {
10132   MESSAGE("doubleNodes");
10133   // iterate on through element and duplicate them (by nodes duplication)
10134   bool res = false;
10135   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10136   for ( ;  elemItr != theElems.end(); ++elemItr )
10137   {
10138     const SMDS_MeshElement* anElem = *elemItr;
10139     if (!anElem)
10140       continue;
10141
10142     bool isDuplicate = false;
10143     // duplicate nodes to duplicate element
10144     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10145     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10146     int ind = 0;
10147     while ( anIter->more() )
10148     {
10149
10150       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10151       SMDS_MeshNode* aNewNode = aCurrNode;
10152       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10153         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10154       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10155       {
10156         // duplicate node
10157         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10158         theNodeNodeMap[ aCurrNode ] = aNewNode;
10159         myLastCreatedNodes.Append( aNewNode );
10160       }
10161       isDuplicate |= (aCurrNode != aNewNode);
10162       newNodes[ ind++ ] = aNewNode;
10163     }
10164     if ( !isDuplicate )
10165       continue;
10166
10167     if ( theIsDoubleElem )
10168       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10169     else
10170       {
10171       MESSAGE("ChangeElementNodes");
10172       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10173       }
10174     res = true;
10175   }
10176   return res;
10177 }
10178
10179 //================================================================================
10180 /*!
10181   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10182   \param theNodes - identifiers of nodes to be doubled
10183   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10184          nodes. If list of element identifiers is empty then nodes are doubled but
10185          they not assigned to elements
10186   \return TRUE if operation has been completed successfully, FALSE otherwise
10187 */
10188 //================================================================================
10189
10190 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10191                                     const std::list< int >& theListOfModifiedElems )
10192 {
10193   MESSAGE("DoubleNodes");
10194   myLastCreatedElems.Clear();
10195   myLastCreatedNodes.Clear();
10196
10197   if ( theListOfNodes.size() == 0 )
10198     return false;
10199
10200   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10201   if ( !aMeshDS )
10202     return false;
10203
10204   // iterate through nodes and duplicate them
10205
10206   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10207
10208   std::list< int >::const_iterator aNodeIter;
10209   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10210   {
10211     int aCurr = *aNodeIter;
10212     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10213     if ( !aNode )
10214       continue;
10215
10216     // duplicate node
10217
10218     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10219     if ( aNewNode )
10220     {
10221       anOldNodeToNewNode[ aNode ] = aNewNode;
10222       myLastCreatedNodes.Append( aNewNode );
10223     }
10224   }
10225
10226   // Create map of new nodes for modified elements
10227
10228   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10229
10230   std::list< int >::const_iterator anElemIter;
10231   for ( anElemIter = theListOfModifiedElems.begin();
10232         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10233   {
10234     int aCurr = *anElemIter;
10235     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10236     if ( !anElem )
10237       continue;
10238
10239     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10240
10241     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10242     int ind = 0;
10243     while ( anIter->more() )
10244     {
10245       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10246       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10247       {
10248         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10249         aNodeArr[ ind++ ] = aNewNode;
10250       }
10251       else
10252         aNodeArr[ ind++ ] = aCurrNode;
10253     }
10254     anElemToNodes[ anElem ] = aNodeArr;
10255   }
10256
10257   // Change nodes of elements
10258
10259   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10260     anElemToNodesIter = anElemToNodes.begin();
10261   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10262   {
10263     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10264     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10265     if ( anElem )
10266       {
10267       MESSAGE("ChangeElementNodes");
10268       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10269       }
10270   }
10271
10272   return true;
10273 }
10274
10275 namespace {
10276
10277   //================================================================================
10278   /*!
10279   \brief Check if element located inside shape
10280   \return TRUE if IN or ON shape, FALSE otherwise
10281   */
10282   //================================================================================
10283
10284   template<class Classifier>
10285   bool isInside(const SMDS_MeshElement* theElem,
10286                 Classifier&             theClassifier,
10287                 const double            theTol)
10288   {
10289     gp_XYZ centerXYZ (0, 0, 0);
10290     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10291     while (aNodeItr->more())
10292       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10293
10294     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10295     theClassifier.Perform(aPnt, theTol);
10296     TopAbs_State aState = theClassifier.State();
10297     return (aState == TopAbs_IN || aState == TopAbs_ON );
10298   }
10299
10300   //================================================================================
10301   /*!
10302    * \brief Classifier of the 3D point on the TopoDS_Face
10303    *        with interaface suitable for isInside()
10304    */
10305   //================================================================================
10306
10307   struct _FaceClassifier
10308   {
10309     Extrema_ExtPS       _extremum;
10310     BRepAdaptor_Surface _surface;
10311     TopAbs_State        _state;
10312
10313     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10314     {
10315       _extremum.Initialize( _surface,
10316                             _surface.FirstUParameter(), _surface.LastUParameter(),
10317                             _surface.FirstVParameter(), _surface.LastVParameter(),
10318                             _surface.Tolerance(), _surface.Tolerance() );
10319     }
10320     void Perform(const gp_Pnt& aPnt, double theTol)
10321     {
10322       _state = TopAbs_OUT;
10323       _extremum.Perform(aPnt);
10324       if ( _extremum.IsDone() )
10325         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10326 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10327           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10328 #else
10329           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10330 #endif
10331     }
10332     TopAbs_State State() const
10333     {
10334       return _state;
10335     }
10336   };
10337 }
10338
10339 //================================================================================
10340 /*!
10341   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10342   This method is the first step of DoubleNodeElemGroupsInRegion.
10343   \param theElems - list of groups of elements (edges or faces) to be replicated
10344   \param theNodesNot - list of groups of nodes not to replicated
10345   \param theShape - shape to detect affected elements (element which geometric center
10346          located on or inside shape). If the shape is null, detection is done on faces orientations
10347          (select elements with a gravity center on the side given by faces normals).
10348          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10349          The replicated nodes should be associated to affected elements.
10350   \return groups of affected elements
10351   \sa DoubleNodeElemGroupsInRegion()
10352  */
10353 //================================================================================
10354
10355 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10356                                                    const TIDSortedElemSet& theNodesNot,
10357                                                    const TopoDS_Shape&     theShape,
10358                                                    TIDSortedElemSet&       theAffectedElems)
10359 {
10360   if ( theShape.IsNull() )
10361   {
10362     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10363     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10364     std::set<const SMDS_MeshElement*> edgesToCheck;
10365     alreadyCheckedNodes.clear();
10366     alreadyCheckedElems.clear();
10367     edgesToCheck.clear();
10368
10369     // --- iterates on elements to be replicated and get elements by back references from their nodes
10370
10371     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10372     int ielem;
10373     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10374     {
10375       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10376       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10377         continue;
10378       gp_XYZ normal;
10379       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10380       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10381       std::set<const SMDS_MeshNode*> nodesElem;
10382       nodesElem.clear();
10383       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10384       while ( nodeItr->more() )
10385       {
10386         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10387         nodesElem.insert(aNode);
10388       }
10389       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10390       for (; nodit != nodesElem.end(); nodit++)
10391       {
10392         MESSAGE("  noeud ");
10393         const SMDS_MeshNode* aNode = *nodit;
10394         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10395           continue;
10396         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10397           continue;
10398         alreadyCheckedNodes.insert(aNode);
10399         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10400         while ( backElemItr->more() )
10401         {
10402           MESSAGE("    backelem ");
10403           const SMDS_MeshElement* curElem = backElemItr->next();
10404           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10405             continue;
10406           if (theElems.find(curElem) != theElems.end())
10407             continue;
10408           alreadyCheckedElems.insert(curElem);
10409           double x=0, y=0, z=0;
10410           int nb = 0;
10411           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10412           while ( nodeItr2->more() )
10413           {
10414             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10415             x += anotherNode->X();
10416             y += anotherNode->Y();
10417             z += anotherNode->Z();
10418             nb++;
10419           }
10420           gp_XYZ p;
10421           p.SetCoord( x/nb -aNode->X(),
10422                       y/nb -aNode->Y(),
10423                       z/nb -aNode->Z() );
10424           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10425           if (normal*p > 0)
10426           {
10427             MESSAGE("    --- inserted")
10428             theAffectedElems.insert( curElem );
10429           }
10430           else if (curElem->GetType() == SMDSAbs_Edge)
10431             edgesToCheck.insert(curElem);
10432         }
10433       }
10434     }
10435     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10436     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10437     for( ; eit != edgesToCheck.end(); eit++)
10438     {
10439       bool onside = true;
10440       const SMDS_MeshElement* anEdge = *eit;
10441       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10442       while ( nodeItr->more() )
10443       {
10444         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10445         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10446         {
10447           onside = false;
10448           break;
10449         }
10450       }
10451       if (onside)
10452       {
10453         MESSAGE("    --- edge onside inserted")
10454         theAffectedElems.insert(anEdge);
10455       }
10456     }
10457   }
10458   else
10459   {
10460     const double aTol = Precision::Confusion();
10461     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10462     auto_ptr<_FaceClassifier>              aFaceClassifier;
10463     if ( theShape.ShapeType() == TopAbs_SOLID )
10464     {
10465       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10466       bsc3d->PerformInfinitePoint(aTol);
10467     }
10468     else if (theShape.ShapeType() == TopAbs_FACE )
10469     {
10470       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10471     }
10472
10473     // iterates on indicated elements and get elements by back references from their nodes
10474     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10475     int ielem;
10476     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10477     {
10478       MESSAGE("element " << ielem++);
10479       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10480       if (!anElem)
10481         continue;
10482       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10483       while ( nodeItr->more() )
10484       {
10485         MESSAGE("  noeud ");
10486         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10487         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10488           continue;
10489         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10490         while ( backElemItr->more() )
10491         {
10492           MESSAGE("    backelem ");
10493           const SMDS_MeshElement* curElem = backElemItr->next();
10494           if ( curElem && theElems.find(curElem) == theElems.end() &&
10495               ( bsc3d.get() ?
10496                 isInside( curElem, *bsc3d, aTol ) :
10497                 isInside( curElem, *aFaceClassifier, aTol )))
10498             theAffectedElems.insert( curElem );
10499         }
10500       }
10501     }
10502   }
10503   return true;
10504 }
10505
10506 //================================================================================
10507 /*!
10508   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10509   \param theElems - group of of elements (edges or faces) to be replicated
10510   \param theNodesNot - group of nodes not to replicate
10511   \param theShape - shape to detect affected elements (element which geometric center
10512   located on or inside shape).
10513   The replicated nodes should be associated to affected elements.
10514   \return TRUE if operation has been completed successfully, FALSE otherwise
10515 */
10516 //================================================================================
10517
10518 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10519                                             const TIDSortedElemSet& theNodesNot,
10520                                             const TopoDS_Shape&     theShape )
10521 {
10522   if ( theShape.IsNull() )
10523     return false;
10524
10525   const double aTol = Precision::Confusion();
10526   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10527   auto_ptr<_FaceClassifier>              aFaceClassifier;
10528   if ( theShape.ShapeType() == TopAbs_SOLID )
10529   {
10530     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10531     bsc3d->PerformInfinitePoint(aTol);
10532   }
10533   else if (theShape.ShapeType() == TopAbs_FACE )
10534   {
10535     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10536   }
10537
10538   // iterates on indicated elements and get elements by back references from their nodes
10539   TIDSortedElemSet anAffected;
10540   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10541   for ( ;  elemItr != theElems.end(); ++elemItr )
10542   {
10543     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10544     if (!anElem)
10545       continue;
10546
10547     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10548     while ( nodeItr->more() )
10549     {
10550       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10551       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10552         continue;
10553       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10554       while ( backElemItr->more() )
10555       {
10556         const SMDS_MeshElement* curElem = backElemItr->next();
10557         if ( curElem && theElems.find(curElem) == theElems.end() &&
10558              ( bsc3d.get() ?
10559                isInside( curElem, *bsc3d, aTol ) :
10560                isInside( curElem, *aFaceClassifier, aTol )))
10561           anAffected.insert( curElem );
10562       }
10563     }
10564   }
10565   return DoubleNodes( theElems, theNodesNot, anAffected );
10566 }
10567
10568 /*!
10569  *  \brief compute an oriented angle between two planes defined by four points.
10570  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10571  *  @param p0 base of the rotation axe
10572  *  @param p1 extremity of the rotation axe
10573  *  @param g1 belongs to the first plane
10574  *  @param g2 belongs to the second plane
10575  */
10576 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10577 {
10578 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10579 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10580 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10581 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10582   gp_Vec vref(p0, p1);
10583   gp_Vec v1(p0, g1);
10584   gp_Vec v2(p0, g2);
10585   gp_Vec n1 = vref.Crossed(v1);
10586   gp_Vec n2 = vref.Crossed(v2);
10587   try {
10588     return n2.AngleWithRef(n1, vref);
10589   }
10590   catch ( Standard_Failure ) {
10591   }
10592   return Max( v1.Magnitude(), v2.Magnitude() );
10593 }
10594
10595 /*!
10596  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10597  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10598  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10599  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10600  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10601  * 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.
10602  * 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.
10603  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10604  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10605  * \param theElems - list of groups of volumes, where a group of volume is a set of
10606  *        SMDS_MeshElements sorted by Id.
10607  * \param createJointElems - if TRUE, create the elements
10608  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10609  *        the boundary between \a theDomains and the rest mesh
10610  * \return TRUE if operation has been completed successfully, FALSE otherwise
10611  */
10612 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10613                                                      bool                                 createJointElems,
10614                                                      bool                                 onAllBoundaries)
10615 {
10616   MESSAGE("----------------------------------------------");
10617   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10618   MESSAGE("----------------------------------------------");
10619
10620   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10621   meshDS->BuildDownWardConnectivity(true);
10622   CHRONO(50);
10623   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10624
10625   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10626   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10627   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10628
10629   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10630   std::map<int,int>celldom; // cell vtkId --> domain
10631   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10632   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10633   faceDomains.clear();
10634   celldom.clear();
10635   cellDomains.clear();
10636   nodeDomains.clear();
10637   std::map<int,int> emptyMap;
10638   std::set<int> emptySet;
10639   emptyMap.clear();
10640
10641   MESSAGE(".. Number of domains :"<<theElems.size());
10642
10643   TIDSortedElemSet theRestDomElems;
10644   const int iRestDom  = -1;
10645   const int idom0     = onAllBoundaries ? iRestDom : 0;
10646   const int nbDomains = theElems.size();
10647
10648   // Check if the domains do not share an element
10649   for (int idom = 0; idom < nbDomains-1; idom++)
10650     {
10651 //       MESSAGE("... Check of domain #" << idom);
10652       const TIDSortedElemSet& domain = theElems[idom];
10653       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10654       for (; elemItr != domain.end(); ++elemItr)
10655         {
10656           const SMDS_MeshElement* anElem = *elemItr;
10657           int idombisdeb = idom + 1 ;
10658           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10659           {
10660             const TIDSortedElemSet& domainbis = theElems[idombis];
10661             if ( domainbis.count(anElem) )
10662             {
10663               MESSAGE(".... Domain #" << idom);
10664               MESSAGE(".... Domain #" << idombis);
10665               throw SALOME_Exception("The domains are not disjoint.");
10666               return false ;
10667             }
10668           }
10669         }
10670     }
10671
10672   for (int idom = 0; idom < nbDomains; idom++)
10673     {
10674
10675       // --- build a map (face to duplicate --> volume to modify)
10676       //     with all the faces shared by 2 domains (group of elements)
10677       //     and corresponding volume of this domain, for each shared face.
10678       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10679
10680       MESSAGE("... Neighbors of domain #" << idom);
10681       const TIDSortedElemSet& domain = theElems[idom];
10682       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10683       for (; elemItr != domain.end(); ++elemItr)
10684         {
10685           const SMDS_MeshElement* anElem = *elemItr;
10686           if (!anElem)
10687             continue;
10688           int vtkId = anElem->getVtkId();
10689           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10690           int neighborsVtkIds[NBMAXNEIGHBORS];
10691           int downIds[NBMAXNEIGHBORS];
10692           unsigned char downTypes[NBMAXNEIGHBORS];
10693           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10694           for (int n = 0; n < nbNeighbors; n++)
10695             {
10696               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10697               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10698               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10699                 {
10700                   bool ok = false ;
10701                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10702                   {
10703                     // MESSAGE("Domain " << idombis);
10704                     const TIDSortedElemSet& domainbis = theElems[idombis];
10705                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10706                   }
10707                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10708                   {
10709                     DownIdType face(downIds[n], downTypes[n]);
10710                     if (!faceDomains[face].count(idom))
10711                       {
10712                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10713                         celldom[vtkId] = idom;
10714                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10715                       }
10716                     if ( !ok )
10717                     {
10718                       theRestDomElems.insert( elem );
10719                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10720                       celldom[neighborsVtkIds[n]] = iRestDom;
10721                     }
10722                   }
10723                 }
10724             }
10725         }
10726     }
10727
10728   //MESSAGE("Number of shared faces " << faceDomains.size());
10729   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10730
10731   // --- explore the shared faces domain by domain,
10732   //     explore the nodes of the face and see if they belong to a cell in the domain,
10733   //     which has only a node or an edge on the border (not a shared face)
10734
10735   for (int idomain = idom0; idomain < nbDomains; idomain++)
10736     {
10737       //MESSAGE("Domain " << idomain);
10738       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10739       itface = faceDomains.begin();
10740       for (; itface != faceDomains.end(); ++itface)
10741         {
10742           const std::map<int, int>& domvol = itface->second;
10743           if (!domvol.count(idomain))
10744             continue;
10745           DownIdType face = itface->first;
10746           //MESSAGE(" --- face " << face.cellId);
10747           std::set<int> oldNodes;
10748           oldNodes.clear();
10749           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10750           std::set<int>::iterator itn = oldNodes.begin();
10751           for (; itn != oldNodes.end(); ++itn)
10752             {
10753               int oldId = *itn;
10754               //MESSAGE("     node " << oldId);
10755               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10756               for (int i=0; i<l.ncells; i++)
10757                 {
10758                   int vtkId = l.cells[i];
10759                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10760                   if (!domain.count(anElem))
10761                     continue;
10762                   int vtkType = grid->GetCellType(vtkId);
10763                   int downId = grid->CellIdToDownId(vtkId);
10764                   if (downId < 0)
10765                     {
10766                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10767                       continue; // not OK at this stage of the algorithm:
10768                                 //no cells created after BuildDownWardConnectivity
10769                     }
10770                   DownIdType aCell(downId, vtkType);
10771                   cellDomains[aCell][idomain] = vtkId;
10772                   celldom[vtkId] = idomain;
10773                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10774                 }
10775             }
10776         }
10777     }
10778
10779   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10780   //     for each shared face, get the nodes
10781   //     for each node, for each domain of the face, create a clone of the node
10782
10783   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10784   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10785   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10786
10787   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10788   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10789   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10790
10791   MESSAGE(".. Duplication of the nodes");
10792   for (int idomain = idom0; idomain < nbDomains; idomain++)
10793     {
10794       itface = faceDomains.begin();
10795       for (; itface != faceDomains.end(); ++itface)
10796         {
10797           const std::map<int, int>& domvol = itface->second;
10798           if (!domvol.count(idomain))
10799             continue;
10800           DownIdType face = itface->first;
10801           //MESSAGE(" --- face " << face.cellId);
10802           std::set<int> oldNodes;
10803           oldNodes.clear();
10804           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10805           std::set<int>::iterator itn = oldNodes.begin();
10806           for (; itn != oldNodes.end(); ++itn)
10807             {
10808               int oldId = *itn;
10809               if (nodeDomains[oldId].empty())
10810                 {
10811                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10812                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10813                 }
10814               std::map<int, int>::const_iterator itdom = domvol.begin();
10815               for (; itdom != domvol.end(); ++itdom)
10816                 {
10817                   int idom = itdom->first;
10818                   //MESSAGE("         domain " << idom);
10819                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10820                     {
10821                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10822                         {
10823                           vector<int> orderedDoms;
10824                           //MESSAGE("multiple node " << oldId);
10825                           if (mutipleNodes.count(oldId))
10826                             orderedDoms = mutipleNodes[oldId];
10827                           else
10828                             {
10829                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10830                               for (; it != nodeDomains[oldId].end(); ++it)
10831                                 orderedDoms.push_back(it->first);
10832                             }
10833                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10834                           //stringstream txt;
10835                           //for (int i=0; i<orderedDoms.size(); i++)
10836                           //  txt << orderedDoms[i] << " ";
10837                           //MESSAGE("orderedDoms " << txt.str());
10838                           mutipleNodes[oldId] = orderedDoms;
10839                         }
10840                       double *coords = grid->GetPoint(oldId);
10841                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10842                       int newId = newNode->getVtkId();
10843                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10844                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10845                     }
10846                 }
10847             }
10848         }
10849     }
10850
10851   MESSAGE(".. Creation of elements");
10852   for (int idomain = idom0; idomain < nbDomains; idomain++)
10853     {
10854       itface = faceDomains.begin();
10855       for (; itface != faceDomains.end(); ++itface)
10856         {
10857           std::map<int, int> domvol = itface->second;
10858           if (!domvol.count(idomain))
10859             continue;
10860           DownIdType face = itface->first;
10861           //MESSAGE(" --- face " << face.cellId);
10862           std::set<int> oldNodes;
10863           oldNodes.clear();
10864           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10865           int nbMultipleNodes = 0;
10866           std::set<int>::iterator itn = oldNodes.begin();
10867           for (; itn != oldNodes.end(); ++itn)
10868             {
10869               int oldId = *itn;
10870               if (mutipleNodes.count(oldId))
10871                 nbMultipleNodes++;
10872             }
10873           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10874             {
10875               //MESSAGE("multiple Nodes detected on a shared face");
10876               int downId = itface->first.cellId;
10877               unsigned char cellType = itface->first.cellType;
10878               // --- shared edge or shared face ?
10879               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10880                 {
10881                   int nodes[3];
10882                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10883                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10884                     if (mutipleNodes.count(nodes[i]))
10885                       if (!mutipleNodesToFace.count(nodes[i]))
10886                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10887                 }
10888               else // shared face (between two volumes)
10889                 {
10890                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10891                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10892                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10893                   for (int ie =0; ie < nbEdges; ie++)
10894                     {
10895                       int nodes[3];
10896                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10897                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10898                         {
10899                           vector<int> vn0 = mutipleNodes[nodes[0]];
10900                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10901                           vector<int> doms;
10902                           for (int i0 = 0; i0 < vn0.size(); i0++)
10903                             for (int i1 = 0; i1 < vn1.size(); i1++)
10904                               if (vn0[i0] == vn1[i1])
10905                                 doms.push_back(vn0[i0]);
10906                           if (doms.size() >2)
10907                             {
10908                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10909                               double *coords = grid->GetPoint(nodes[0]);
10910                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10911                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10912                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10913                               gp_Pnt gref;
10914                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10915                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10916                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10917                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10918                               for (int id=0; id < doms.size(); id++)
10919                                 {
10920                                   int idom = doms[id];
10921                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10922                                   for (int ivol=0; ivol<nbvol; ivol++)
10923                                     {
10924                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10925                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10926                                       if (domain.count(elem))
10927                                         {
10928                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10929                                           domvol[idom] = svol;
10930                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10931                                           double values[3];
10932                                           vtkIdType npts = 0;
10933                                           vtkIdType* pts = 0;
10934                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10935                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10936                                           if (id ==0)
10937                                             {
10938                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10939                                               angleDom[idom] = 0;
10940                                             }
10941                                           else
10942                                             {
10943                                               gp_Pnt g(values[0], values[1], values[2]);
10944                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10945                                               //MESSAGE("  angle=" << angleDom[idom]);
10946                                             }
10947                                           break;
10948                                         }
10949                                     }
10950                                 }
10951                               map<double, int> sortedDom; // sort domains by angle
10952                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10953                                 sortedDom[ia->second] = ia->first;
10954                               vector<int> vnodes;
10955                               vector<int> vdom;
10956                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10957                                 {
10958                                   vdom.push_back(ib->second);
10959                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10960                                 }
10961                               for (int ino = 0; ino < nbNodes; ino++)
10962                                 vnodes.push_back(nodes[ino]);
10963                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10964                             }
10965                         }
10966                     }
10967                 }
10968             }
10969         }
10970     }
10971
10972   // --- iterate on shared faces (volumes to modify, face to extrude)
10973   //     get node id's of the face (id SMDS = id VTK)
10974   //     create flat element with old and new nodes if requested
10975
10976   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10977   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10978
10979   std::map<int, std::map<long,int> > nodeQuadDomains;
10980   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10981
10982   MESSAGE(".. Creation of elements: simple junction");
10983   if (createJointElems)
10984     {
10985       int idg;
10986       string joints2DName = "joints2D";
10987       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10988       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10989       string joints3DName = "joints3D";
10990       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10991       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10992
10993       itface = faceDomains.begin();
10994       for (; itface != faceDomains.end(); ++itface)
10995         {
10996           DownIdType face = itface->first;
10997           std::set<int> oldNodes;
10998           std::set<int>::iterator itn;
10999           oldNodes.clear();
11000           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11001
11002           std::map<int, int> domvol = itface->second;
11003           std::map<int, int>::iterator itdom = domvol.begin();
11004           int dom1 = itdom->first;
11005           int vtkVolId = itdom->second;
11006           itdom++;
11007           int dom2 = itdom->first;
11008           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11009                                                              nodeQuadDomains);
11010           stringstream grpname;
11011           grpname << "j_";
11012           if (dom1 < dom2)
11013             grpname << dom1 << "_" << dom2;
11014           else
11015             grpname << dom2 << "_" << dom1;
11016           string namegrp = grpname.str();
11017           if (!mapOfJunctionGroups.count(namegrp))
11018             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11019           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11020           if (sgrp)
11021             sgrp->Add(vol->GetID());
11022           if (vol->GetType() == SMDSAbs_Volume)
11023             joints3DGrp->Add(vol->GetID());
11024           else if (vol->GetType() == SMDSAbs_Face)
11025             joints2DGrp->Add(vol->GetID());
11026         }
11027     }
11028
11029   // --- create volumes on multiple domain intersection if requested
11030   //     iterate on mutipleNodesToFace
11031   //     iterate on edgesMultiDomains
11032
11033   MESSAGE(".. Creation of elements: multiple junction");
11034   if (createJointElems)
11035     {
11036       // --- iterate on mutipleNodesToFace
11037
11038       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11039       for (; itn != mutipleNodesToFace.end(); ++itn)
11040         {
11041           int node = itn->first;
11042           vector<int> orderDom = itn->second;
11043           vector<vtkIdType> orderedNodes;
11044           for (int idom = 0; idom <orderDom.size(); idom++)
11045             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11046             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11047
11048             stringstream grpname;
11049             grpname << "m2j_";
11050             grpname << 0 << "_" << 0;
11051             int idg;
11052             string namegrp = grpname.str();
11053             if (!mapOfJunctionGroups.count(namegrp))
11054               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11055             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11056             if (sgrp)
11057               sgrp->Add(face->GetID());
11058         }
11059
11060       // --- iterate on edgesMultiDomains
11061
11062       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11063       for (; ite != edgesMultiDomains.end(); ++ite)
11064         {
11065           vector<int> nodes = ite->first;
11066           vector<int> orderDom = ite->second;
11067           vector<vtkIdType> orderedNodes;
11068           if (nodes.size() == 2)
11069             {
11070               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11071               for (int ino=0; ino < nodes.size(); ino++)
11072                 if (orderDom.size() == 3)
11073                   for (int idom = 0; idom <orderDom.size(); idom++)
11074                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11075                 else
11076                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11077                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11078               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11079
11080               int idg;
11081               string namegrp = "jointsMultiples";
11082               if (!mapOfJunctionGroups.count(namegrp))
11083                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11084               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11085               if (sgrp)
11086                 sgrp->Add(vol->GetID());
11087             }
11088           else
11089             {
11090               INFOS("Quadratic multiple joints not implemented");
11091               // TODO quadratic nodes
11092             }
11093         }
11094     }
11095
11096   // --- list the explicit faces and edges of the mesh that need to be modified,
11097   //     i.e. faces and edges built with one or more duplicated nodes.
11098   //     associate these faces or edges to their corresponding domain.
11099   //     only the first domain found is kept when a face or edge is shared
11100
11101   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11102   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11103   faceOrEdgeDom.clear();
11104   feDom.clear();
11105
11106   MESSAGE(".. Modification of elements");
11107   for (int idomain = idom0; idomain < nbDomains; idomain++)
11108     {
11109       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11110       for (; itnod != nodeDomains.end(); ++itnod)
11111         {
11112           int oldId = itnod->first;
11113           //MESSAGE("     node " << oldId);
11114           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11115           for (int i = 0; i < l.ncells; i++)
11116             {
11117               int vtkId = l.cells[i];
11118               int vtkType = grid->GetCellType(vtkId);
11119               int downId = grid->CellIdToDownId(vtkId);
11120               if (downId < 0)
11121                 continue; // new cells: not to be modified
11122               DownIdType aCell(downId, vtkType);
11123               int volParents[1000];
11124               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11125               for (int j = 0; j < nbvol; j++)
11126                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11127                   if (!feDom.count(vtkId))
11128                     {
11129                       feDom[vtkId] = idomain;
11130                       faceOrEdgeDom[aCell] = emptyMap;
11131                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11132                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11133                       //        << " type " << vtkType << " downId " << downId);
11134                     }
11135             }
11136         }
11137     }
11138
11139   // --- iterate on shared faces (volumes to modify, face to extrude)
11140   //     get node id's of the face
11141   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11142
11143   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11144   for (int m=0; m<3; m++)
11145     {
11146       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11147       itface = (*amap).begin();
11148       for (; itface != (*amap).end(); ++itface)
11149         {
11150           DownIdType face = itface->first;
11151           std::set<int> oldNodes;
11152           std::set<int>::iterator itn;
11153           oldNodes.clear();
11154           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11155           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11156           std::map<int, int> localClonedNodeIds;
11157
11158           std::map<int, int> domvol = itface->second;
11159           std::map<int, int>::iterator itdom = domvol.begin();
11160           for (; itdom != domvol.end(); ++itdom)
11161             {
11162               int idom = itdom->first;
11163               int vtkVolId = itdom->second;
11164               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11165               localClonedNodeIds.clear();
11166               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11167                 {
11168                   int oldId = *itn;
11169                   if (nodeDomains[oldId].count(idom))
11170                     {
11171                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11172                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11173                     }
11174                 }
11175               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11176             }
11177         }
11178     }
11179
11180   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11181   grid->BuildLinks();
11182
11183   CHRONOSTOP(50);
11184   counters::stats();
11185   return true;
11186 }
11187
11188 /*!
11189  * \brief Double nodes on some external faces and create flat elements.
11190  * Flat elements are mainly used by some types of mechanic calculations.
11191  *
11192  * Each group of the list must be constituted of faces.
11193  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11194  * @param theElems - list of groups of faces, where a group of faces is a set of
11195  * SMDS_MeshElements sorted by Id.
11196  * @return TRUE if operation has been completed successfully, FALSE otherwise
11197  */
11198 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11199 {
11200   MESSAGE("-------------------------------------------------");
11201   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11202   MESSAGE("-------------------------------------------------");
11203
11204   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11205
11206   // --- For each group of faces
11207   //     duplicate the nodes, create a flat element based on the face
11208   //     replace the nodes of the faces by their clones
11209
11210   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11211   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11212   clonedNodes.clear();
11213   intermediateNodes.clear();
11214   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11215   mapOfJunctionGroups.clear();
11216
11217   for (int idom = 0; idom < theElems.size(); idom++)
11218     {
11219       const TIDSortedElemSet& domain = theElems[idom];
11220       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11221       for (; elemItr != domain.end(); ++elemItr)
11222         {
11223           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11224           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11225           if (!aFace)
11226             continue;
11227           // MESSAGE("aFace=" << aFace->GetID());
11228           bool isQuad = aFace->IsQuadratic();
11229           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11230
11231           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11232
11233           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11234           while (nodeIt->more())
11235             {
11236               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11237               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11238               if (isMedium)
11239                 ln2.push_back(node);
11240               else
11241                 ln0.push_back(node);
11242
11243               const SMDS_MeshNode* clone = 0;
11244               if (!clonedNodes.count(node))
11245                 {
11246                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11247                   clonedNodes[node] = clone;
11248                 }
11249               else
11250                 clone = clonedNodes[node];
11251
11252               if (isMedium)
11253                 ln3.push_back(clone);
11254               else
11255                 ln1.push_back(clone);
11256
11257               const SMDS_MeshNode* inter = 0;
11258               if (isQuad && (!isMedium))
11259                 {
11260                   if (!intermediateNodes.count(node))
11261                     {
11262                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11263                       intermediateNodes[node] = inter;
11264                     }
11265                   else
11266                     inter = intermediateNodes[node];
11267                   ln4.push_back(inter);
11268                 }
11269             }
11270
11271           // --- extrude the face
11272
11273           vector<const SMDS_MeshNode*> ln;
11274           SMDS_MeshVolume* vol = 0;
11275           vtkIdType aType = aFace->GetVtkType();
11276           switch (aType)
11277           {
11278             case VTK_TRIANGLE:
11279               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11280               // MESSAGE("vol prism " << vol->GetID());
11281               ln.push_back(ln1[0]);
11282               ln.push_back(ln1[1]);
11283               ln.push_back(ln1[2]);
11284               break;
11285             case VTK_QUAD:
11286               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11287               // MESSAGE("vol hexa " << vol->GetID());
11288               ln.push_back(ln1[0]);
11289               ln.push_back(ln1[1]);
11290               ln.push_back(ln1[2]);
11291               ln.push_back(ln1[3]);
11292               break;
11293             case VTK_QUADRATIC_TRIANGLE:
11294               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11295                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11296               // MESSAGE("vol quad prism " << vol->GetID());
11297               ln.push_back(ln1[0]);
11298               ln.push_back(ln1[1]);
11299               ln.push_back(ln1[2]);
11300               ln.push_back(ln3[0]);
11301               ln.push_back(ln3[1]);
11302               ln.push_back(ln3[2]);
11303               break;
11304             case VTK_QUADRATIC_QUAD:
11305 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11306 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11307 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11308               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11309                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11310                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11311               // MESSAGE("vol quad hexa " << vol->GetID());
11312               ln.push_back(ln1[0]);
11313               ln.push_back(ln1[1]);
11314               ln.push_back(ln1[2]);
11315               ln.push_back(ln1[3]);
11316               ln.push_back(ln3[0]);
11317               ln.push_back(ln3[1]);
11318               ln.push_back(ln3[2]);
11319               ln.push_back(ln3[3]);
11320               break;
11321             case VTK_POLYGON:
11322               break;
11323             default:
11324               break;
11325           }
11326
11327           if (vol)
11328             {
11329               stringstream grpname;
11330               grpname << "jf_";
11331               grpname << idom;
11332               int idg;
11333               string namegrp = grpname.str();
11334               if (!mapOfJunctionGroups.count(namegrp))
11335                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11336               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11337               if (sgrp)
11338                 sgrp->Add(vol->GetID());
11339             }
11340
11341           // --- modify the face
11342
11343           aFace->ChangeNodes(&ln[0], ln.size());
11344         }
11345     }
11346   return true;
11347 }
11348
11349 /*!
11350  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11351  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11352  *  groups of faces to remove inside the object, (idem edges).
11353  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11354  */
11355 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11356                                       const TopoDS_Shape& theShape,
11357                                       SMESH_NodeSearcher* theNodeSearcher,
11358                                       const char* groupName,
11359                                       std::vector<double>&   nodesCoords,
11360                                       std::vector<std::vector<int> >& listOfListOfNodes)
11361 {
11362   MESSAGE("--------------------------------");
11363   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11364   MESSAGE("--------------------------------");
11365
11366   // --- zone of volumes to remove is given :
11367   //     1 either by a geom shape (one or more vertices) and a radius,
11368   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11369   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11370   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11371   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11372   //     defined by it's name.
11373
11374   SMESHDS_GroupBase* groupDS = 0;
11375   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11376   while ( groupIt->more() )
11377     {
11378       groupDS = 0;
11379       SMESH_Group * group = groupIt->next();
11380       if ( !group ) continue;
11381       groupDS = group->GetGroupDS();
11382       if ( !groupDS || groupDS->IsEmpty() ) continue;
11383       std::string grpName = group->GetName();
11384       //MESSAGE("grpName=" << grpName);
11385       if (grpName == groupName)
11386         break;
11387       else
11388         groupDS = 0;
11389     }
11390
11391   bool isNodeGroup = false;
11392   bool isNodeCoords = false;
11393   if (groupDS)
11394     {
11395       if (groupDS->GetType() != SMDSAbs_Node)
11396         return;
11397       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11398     }
11399
11400   if (nodesCoords.size() > 0)
11401     isNodeCoords = true; // a list o nodes given by their coordinates
11402   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11403
11404   // --- define groups to build
11405
11406   int idg; // --- group of SMDS volumes
11407   string grpvName = groupName;
11408   grpvName += "_vol";
11409   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11410   if (!grp)
11411     {
11412       MESSAGE("group not created " << grpvName);
11413       return;
11414     }
11415   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11416
11417   int idgs; // --- group of SMDS faces on the skin
11418   string grpsName = groupName;
11419   grpsName += "_skin";
11420   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11421   if (!grps)
11422     {
11423       MESSAGE("group not created " << grpsName);
11424       return;
11425     }
11426   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11427
11428   int idgi; // --- group of SMDS faces internal (several shapes)
11429   string grpiName = groupName;
11430   grpiName += "_internalFaces";
11431   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11432   if (!grpi)
11433     {
11434       MESSAGE("group not created " << grpiName);
11435       return;
11436     }
11437   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11438
11439   int idgei; // --- group of SMDS faces internal (several shapes)
11440   string grpeiName = groupName;
11441   grpeiName += "_internalEdges";
11442   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11443   if (!grpei)
11444     {
11445       MESSAGE("group not created " << grpeiName);
11446       return;
11447     }
11448   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11449
11450   // --- build downward connectivity
11451
11452   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11453   meshDS->BuildDownWardConnectivity(true);
11454   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11455
11456   // --- set of volumes detected inside
11457
11458   std::set<int> setOfInsideVol;
11459   std::set<int> setOfVolToCheck;
11460
11461   std::vector<gp_Pnt> gpnts;
11462   gpnts.clear();
11463
11464   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11465     {
11466       MESSAGE("group of nodes provided");
11467       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11468       while ( elemIt->more() )
11469         {
11470           const SMDS_MeshElement* elem = elemIt->next();
11471           if (!elem)
11472             continue;
11473           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11474           if (!node)
11475             continue;
11476           SMDS_MeshElement* vol = 0;
11477           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11478           while (volItr->more())
11479             {
11480               vol = (SMDS_MeshElement*)volItr->next();
11481               setOfInsideVol.insert(vol->getVtkId());
11482               sgrp->Add(vol->GetID());
11483             }
11484         }
11485     }
11486   else if (isNodeCoords)
11487     {
11488       MESSAGE("list of nodes coordinates provided");
11489       int i = 0;
11490       int k = 0;
11491       while (i < nodesCoords.size()-2)
11492         {
11493           double x = nodesCoords[i++];
11494           double y = nodesCoords[i++];
11495           double z = nodesCoords[i++];
11496           gp_Pnt p = gp_Pnt(x, y ,z);
11497           gpnts.push_back(p);
11498           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11499           k++;
11500         }
11501     }
11502   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11503     {
11504       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11505       TopTools_IndexedMapOfShape vertexMap;
11506       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11507       gp_Pnt p = gp_Pnt(0,0,0);
11508       if (vertexMap.Extent() < 1)
11509         return;
11510
11511       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11512         {
11513           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11514           p = BRep_Tool::Pnt(vertex);
11515           gpnts.push_back(p);
11516           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11517         }
11518     }
11519
11520   if (gpnts.size() > 0)
11521     {
11522       int nodeId = 0;
11523       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11524       if (startNode)
11525         nodeId = startNode->GetID();
11526       MESSAGE("nodeId " << nodeId);
11527
11528       double radius2 = radius*radius;
11529       MESSAGE("radius2 " << radius2);
11530
11531       // --- volumes on start node
11532
11533       setOfVolToCheck.clear();
11534       SMDS_MeshElement* startVol = 0;
11535       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11536       while (volItr->more())
11537         {
11538           startVol = (SMDS_MeshElement*)volItr->next();
11539           setOfVolToCheck.insert(startVol->getVtkId());
11540         }
11541       if (setOfVolToCheck.empty())
11542         {
11543           MESSAGE("No volumes found");
11544           return;
11545         }
11546
11547       // --- starting with central volumes then their neighbors, check if they are inside
11548       //     or outside the domain, until no more new neighbor volume is inside.
11549       //     Fill the group of inside volumes
11550
11551       std::map<int, double> mapOfNodeDistance2;
11552       mapOfNodeDistance2.clear();
11553       std::set<int> setOfOutsideVol;
11554       while (!setOfVolToCheck.empty())
11555         {
11556           std::set<int>::iterator it = setOfVolToCheck.begin();
11557           int vtkId = *it;
11558           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11559           bool volInside = false;
11560           vtkIdType npts = 0;
11561           vtkIdType* pts = 0;
11562           grid->GetCellPoints(vtkId, npts, pts);
11563           for (int i=0; i<npts; i++)
11564             {
11565               double distance2 = 0;
11566               if (mapOfNodeDistance2.count(pts[i]))
11567                 {
11568                   distance2 = mapOfNodeDistance2[pts[i]];
11569                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11570                 }
11571               else
11572                 {
11573                   double *coords = grid->GetPoint(pts[i]);
11574                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11575                   distance2 = 1.E40;
11576                   for (int j=0; j<gpnts.size(); j++)
11577                     {
11578                       double d2 = aPoint.SquareDistance(gpnts[j]);
11579                       if (d2 < distance2)
11580                         {
11581                           distance2 = d2;
11582                           if (distance2 < radius2)
11583                             break;
11584                         }
11585                     }
11586                   mapOfNodeDistance2[pts[i]] = distance2;
11587                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11588                 }
11589               if (distance2 < radius2)
11590                 {
11591                   volInside = true; // one or more nodes inside the domain
11592                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11593                   break;
11594                 }
11595             }
11596           if (volInside)
11597             {
11598               setOfInsideVol.insert(vtkId);
11599               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11600               int neighborsVtkIds[NBMAXNEIGHBORS];
11601               int downIds[NBMAXNEIGHBORS];
11602               unsigned char downTypes[NBMAXNEIGHBORS];
11603               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11604               for (int n = 0; n < nbNeighbors; n++)
11605                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11606                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11607             }
11608           else
11609             {
11610               setOfOutsideVol.insert(vtkId);
11611               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11612             }
11613           setOfVolToCheck.erase(vtkId);
11614         }
11615     }
11616
11617   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11618   //     If yes, add the volume to the inside set
11619
11620   bool addedInside = true;
11621   std::set<int> setOfVolToReCheck;
11622   while (addedInside)
11623     {
11624       MESSAGE(" --------------------------- re check");
11625       addedInside = false;
11626       std::set<int>::iterator itv = setOfInsideVol.begin();
11627       for (; itv != setOfInsideVol.end(); ++itv)
11628         {
11629           int vtkId = *itv;
11630           int neighborsVtkIds[NBMAXNEIGHBORS];
11631           int downIds[NBMAXNEIGHBORS];
11632           unsigned char downTypes[NBMAXNEIGHBORS];
11633           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11634           for (int n = 0; n < nbNeighbors; n++)
11635             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11636               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11637         }
11638       setOfVolToCheck = setOfVolToReCheck;
11639       setOfVolToReCheck.clear();
11640       while  (!setOfVolToCheck.empty())
11641         {
11642           std::set<int>::iterator it = setOfVolToCheck.begin();
11643           int vtkId = *it;
11644           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11645             {
11646               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11647               int countInside = 0;
11648               int neighborsVtkIds[NBMAXNEIGHBORS];
11649               int downIds[NBMAXNEIGHBORS];
11650               unsigned char downTypes[NBMAXNEIGHBORS];
11651               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11652               for (int n = 0; n < nbNeighbors; n++)
11653                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11654                   countInside++;
11655               MESSAGE("countInside " << countInside);
11656               if (countInside > 1)
11657                 {
11658                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11659                   setOfInsideVol.insert(vtkId);
11660                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11661                   addedInside = true;
11662                 }
11663               else
11664                 setOfVolToReCheck.insert(vtkId);
11665             }
11666           setOfVolToCheck.erase(vtkId);
11667         }
11668     }
11669
11670   // --- map of Downward faces at the boundary, inside the global volume
11671   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11672   //     fill group of SMDS faces inside the volume (when several volume shapes)
11673   //     fill group of SMDS faces on the skin of the global volume (if skin)
11674
11675   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11676   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11677   std::set<int>::iterator it = setOfInsideVol.begin();
11678   for (; it != setOfInsideVol.end(); ++it)
11679     {
11680       int vtkId = *it;
11681       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11682       int neighborsVtkIds[NBMAXNEIGHBORS];
11683       int downIds[NBMAXNEIGHBORS];
11684       unsigned char downTypes[NBMAXNEIGHBORS];
11685       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11686       for (int n = 0; n < nbNeighbors; n++)
11687         {
11688           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11689           if (neighborDim == 3)
11690             {
11691               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11692                 {
11693                   DownIdType face(downIds[n], downTypes[n]);
11694                   boundaryFaces[face] = vtkId;
11695                 }
11696               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11697               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11698               if (vtkFaceId >= 0)
11699                 {
11700                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11701                   // find also the smds edges on this face
11702                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11703                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11704                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11705                   for (int i = 0; i < nbEdges; i++)
11706                     {
11707                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11708                       if (vtkEdgeId >= 0)
11709                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11710                     }
11711                 }
11712             }
11713           else if (neighborDim == 2) // skin of the volume
11714             {
11715               DownIdType face(downIds[n], downTypes[n]);
11716               skinFaces[face] = vtkId;
11717               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11718               if (vtkFaceId >= 0)
11719                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11720             }
11721         }
11722     }
11723
11724   // --- identify the edges constituting the wire of each subshape on the skin
11725   //     define polylines with the nodes of edges, equivalent to wires
11726   //     project polylines on subshapes, and partition, to get geom faces
11727
11728   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11729   std::set<int> emptySet;
11730   emptySet.clear();
11731   std::set<int> shapeIds;
11732
11733   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11734   while (itelem->more())
11735     {
11736       const SMDS_MeshElement *elem = itelem->next();
11737       int shapeId = elem->getshapeId();
11738       int vtkId = elem->getVtkId();
11739       if (!shapeIdToVtkIdSet.count(shapeId))
11740         {
11741           shapeIdToVtkIdSet[shapeId] = emptySet;
11742           shapeIds.insert(shapeId);
11743         }
11744       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11745     }
11746
11747   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11748   std::set<DownIdType, DownIdCompare> emptyEdges;
11749   emptyEdges.clear();
11750
11751   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11752   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11753     {
11754       int shapeId = itShape->first;
11755       MESSAGE(" --- Shape ID --- "<< shapeId);
11756       shapeIdToEdges[shapeId] = emptyEdges;
11757
11758       std::vector<int> nodesEdges;
11759
11760       std::set<int>::iterator its = itShape->second.begin();
11761       for (; its != itShape->second.end(); ++its)
11762         {
11763           int vtkId = *its;
11764           MESSAGE("     " << vtkId);
11765           int neighborsVtkIds[NBMAXNEIGHBORS];
11766           int downIds[NBMAXNEIGHBORS];
11767           unsigned char downTypes[NBMAXNEIGHBORS];
11768           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11769           for (int n = 0; n < nbNeighbors; n++)
11770             {
11771               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11772                 continue;
11773               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11774               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11775               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11776                 {
11777                   DownIdType edge(downIds[n], downTypes[n]);
11778                   if (!shapeIdToEdges[shapeId].count(edge))
11779                     {
11780                       shapeIdToEdges[shapeId].insert(edge);
11781                       int vtkNodeId[3];
11782                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11783                       nodesEdges.push_back(vtkNodeId[0]);
11784                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11785                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11786                     }
11787                 }
11788             }
11789         }
11790
11791       std::list<int> order;
11792       order.clear();
11793       if (nodesEdges.size() > 0)
11794         {
11795           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11796           nodesEdges[0] = -1;
11797           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11798           nodesEdges[1] = -1; // do not reuse this edge
11799           bool found = true;
11800           while (found)
11801             {
11802               int nodeTofind = order.back(); // try first to push back
11803               int i = 0;
11804               for (i = 0; i<nodesEdges.size(); i++)
11805                 if (nodesEdges[i] == nodeTofind)
11806                   break;
11807               if (i == nodesEdges.size())
11808                 found = false; // no follower found on back
11809               else
11810                 {
11811                   if (i%2) // odd ==> use the previous one
11812                     if (nodesEdges[i-1] < 0)
11813                       found = false;
11814                     else
11815                       {
11816                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11817                         nodesEdges[i-1] = -1;
11818                       }
11819                   else // even ==> use the next one
11820                     if (nodesEdges[i+1] < 0)
11821                       found = false;
11822                     else
11823                       {
11824                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11825                         nodesEdges[i+1] = -1;
11826                       }
11827                 }
11828               if (found)
11829                 continue;
11830               // try to push front
11831               found = true;
11832               nodeTofind = order.front(); // try to push front
11833               for (i = 0; i<nodesEdges.size(); i++)
11834                 if (nodesEdges[i] == nodeTofind)
11835                   break;
11836               if (i == nodesEdges.size())
11837                 {
11838                   found = false; // no predecessor found on front
11839                   continue;
11840                 }
11841               if (i%2) // odd ==> use the previous one
11842                 if (nodesEdges[i-1] < 0)
11843                   found = false;
11844                 else
11845                   {
11846                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11847                     nodesEdges[i-1] = -1;
11848                   }
11849               else // even ==> use the next one
11850                 if (nodesEdges[i+1] < 0)
11851                   found = false;
11852                 else
11853                   {
11854                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11855                     nodesEdges[i+1] = -1;
11856                   }
11857             }
11858         }
11859
11860
11861       std::vector<int> nodes;
11862       nodes.push_back(shapeId);
11863       std::list<int>::iterator itl = order.begin();
11864       for (; itl != order.end(); itl++)
11865         {
11866           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11867           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11868         }
11869       listOfListOfNodes.push_back(nodes);
11870     }
11871
11872   //     partition geom faces with blocFissure
11873   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11874   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11875
11876   return;
11877 }
11878
11879
11880 //================================================================================
11881 /*!
11882  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11883  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11884  * \return TRUE if operation has been completed successfully, FALSE otherwise
11885  */
11886 //================================================================================
11887
11888 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11889 {
11890   // iterates on volume elements and detect all free faces on them
11891   SMESHDS_Mesh* aMesh = GetMeshDS();
11892   if (!aMesh)
11893     return false;
11894   //bool res = false;
11895   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11896   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11897   while(vIt->more())
11898   {
11899     const SMDS_MeshVolume* volume = vIt->next();
11900     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11901     vTool.SetExternalNormal();
11902     //const bool isPoly = volume->IsPoly();
11903     const int iQuad = volume->IsQuadratic();
11904     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11905     {
11906       if (!vTool.IsFreeFace(iface))
11907         continue;
11908       nbFree++;
11909       vector<const SMDS_MeshNode *> nodes;
11910       int nbFaceNodes = vTool.NbFaceNodes(iface);
11911       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11912       int inode = 0;
11913       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11914         nodes.push_back(faceNodes[inode]);
11915       if (iQuad) { // add medium nodes
11916         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11917           nodes.push_back(faceNodes[inode]);
11918         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11919           nodes.push_back(faceNodes[8]);
11920       }
11921       // add new face based on volume nodes
11922       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11923         nbExisted++;
11924         continue; // face already exsist
11925       }
11926       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11927       nbCreated++;
11928     }
11929   }
11930   return ( nbFree==(nbExisted+nbCreated) );
11931 }
11932
11933 namespace
11934 {
11935   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11936   {
11937     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11938       return n;
11939     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11940   }
11941 }
11942 //================================================================================
11943 /*!
11944  * \brief Creates missing boundary elements
11945  *  \param elements - elements whose boundary is to be checked
11946  *  \param dimension - defines type of boundary elements to create
11947  *  \param group - a group to store created boundary elements in
11948  *  \param targetMesh - a mesh to store created boundary elements in
11949  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11950  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11951  *                                boundary elements will be copied into the targetMesh
11952  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11953  *                                boundary elements will be added into the new group
11954  *  \param aroundElements - if true, elements will be created on boundary of given
11955  *                          elements else, on boundary of the whole mesh.
11956  * \return nb of added boundary elements
11957  */
11958 //================================================================================
11959
11960 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11961                                        Bnd_Dimension           dimension,
11962                                        SMESH_Group*            group/*=0*/,
11963                                        SMESH_Mesh*             targetMesh/*=0*/,
11964                                        bool                    toCopyElements/*=false*/,
11965                                        bool                    toCopyExistingBoundary/*=false*/,
11966                                        bool                    toAddExistingBondary/*= false*/,
11967                                        bool                    aroundElements/*= false*/)
11968 {
11969   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11970   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11971   // hope that all elements are of the same type, do not check them all
11972   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11973     throw SALOME_Exception(LOCALIZED("wrong element type"));
11974
11975   if ( !targetMesh )
11976     toCopyElements = toCopyExistingBoundary = false;
11977
11978   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11979   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11980   int nbAddedBnd = 0;
11981
11982   // editor adding present bnd elements and optionally holding elements to add to the group
11983   SMESH_MeshEditor* presentEditor;
11984   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11985   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11986
11987   SMESH_MesherHelper helper( *myMesh );
11988   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11989   SMDS_VolumeTool vTool;
11990   TIDSortedElemSet avoidSet;
11991   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11992   int inode;
11993
11994   typedef vector<const SMDS_MeshNode*> TConnectivity;
11995
11996   SMDS_ElemIteratorPtr eIt;
11997   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11998   else                  eIt = elemSetIterator( elements );
11999
12000   while (eIt->more())
12001   {
12002     const SMDS_MeshElement* elem = eIt->next();
12003     const int              iQuad = elem->IsQuadratic();
12004
12005     // ------------------------------------------------------------------------------------
12006     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12007     // ------------------------------------------------------------------------------------
12008     vector<const SMDS_MeshElement*> presentBndElems;
12009     vector<TConnectivity>           missingBndElems;
12010     TConnectivity nodes, elemNodes;
12011     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12012     {
12013       vTool.SetExternalNormal();
12014       const SMDS_MeshElement* otherVol = 0;
12015       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12016       {
12017         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12018              ( !aroundElements || elements.count( otherVol )))
12019           continue;
12020         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12021         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12022         if ( missType == SMDSAbs_Edge ) // boundary edges
12023         {
12024           nodes.resize( 2+iQuad );
12025           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12026           {
12027             for ( int j = 0; j < nodes.size(); ++j )
12028               nodes[j] =nn[i+j];
12029             if ( const SMDS_MeshElement* edge =
12030                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12031               presentBndElems.push_back( edge );
12032             else
12033               missingBndElems.push_back( nodes );
12034           }
12035         }
12036         else // boundary face
12037         {
12038           nodes.clear();
12039           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12040             nodes.push_back( nn[inode] ); // add corner nodes
12041           if (iQuad)
12042             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12043               nodes.push_back( nn[inode] ); // add medium nodes
12044           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12045           if ( iCenter > 0 )
12046             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12047
12048           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12049                                                                SMDSAbs_Face, /*noMedium=*/false ))
12050             presentBndElems.push_back( f );
12051           else
12052             missingBndElems.push_back( nodes );
12053
12054           if ( targetMesh != myMesh )
12055           {
12056             // add 1D elements on face boundary to be added to a new mesh
12057             const SMDS_MeshElement* edge;
12058             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12059             {
12060               if ( iQuad )
12061                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12062               else
12063                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12064               if ( edge && avoidSet.insert( edge ).second )
12065                 presentBndElems.push_back( edge );
12066             }
12067           }
12068         }
12069       }
12070     }
12071     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12072     {
12073       avoidSet.clear(), avoidSet.insert( elem );
12074       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12075                         SMDS_MeshElement::iterator() );
12076       elemNodes.push_back( elemNodes[0] );
12077       nodes.resize( 2 + iQuad );
12078       const int nbLinks = elem->NbCornerNodes();
12079       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12080       {
12081         nodes[0] = elemNodes[iN];
12082         nodes[1] = elemNodes[iN+1+iQuad];
12083         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12084           continue; // not free link
12085
12086         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12087         if ( const SMDS_MeshElement* edge =
12088              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12089           presentBndElems.push_back( edge );
12090         else
12091           missingBndElems.push_back( nodes );
12092       }
12093     }
12094
12095     // ---------------------------------
12096     // 2. Add missing boundary elements
12097     // ---------------------------------
12098     if ( targetMesh != myMesh )
12099       // instead of making a map of nodes in this mesh and targetMesh,
12100       // we create nodes with same IDs.
12101       for ( int i = 0; i < missingBndElems.size(); ++i )
12102       {
12103         TConnectivity& srcNodes = missingBndElems[i];
12104         TConnectivity  nodes( srcNodes.size() );
12105         for ( inode = 0; inode < nodes.size(); ++inode )
12106           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12107         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12108                                                                    missType,
12109                                                                    /*noMedium=*/false))
12110           continue;
12111         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12112         ++nbAddedBnd;
12113       }
12114     else
12115       for ( int i = 0; i < missingBndElems.size(); ++i )
12116       {
12117         TConnectivity& nodes = missingBndElems[i];
12118         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12119                                                                    missType,
12120                                                                    /*noMedium=*/false))
12121           continue;
12122         SMDS_MeshElement* elem =
12123           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12124         ++nbAddedBnd;
12125
12126         // try to set a new element to a shape
12127         if ( myMesh->HasShapeToMesh() )
12128         {
12129           bool ok = true;
12130           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12131           const int nbN = nodes.size() / (iQuad+1 );
12132           for ( inode = 0; inode < nbN && ok; ++inode )
12133           {
12134             pair<int, TopAbs_ShapeEnum> i_stype =
12135               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12136             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12137               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12138           }
12139           if ( ok && mediumShapes.size() > 1 )
12140           {
12141             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12142             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12143             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12144             {
12145               if (( ok = ( stype_i->first != stype_i_0.first )))
12146                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12147                                         aMesh->IndexToShape( stype_i_0.second ));
12148             }
12149           }
12150           if ( ok && mediumShapes.begin()->first == missShapeType )
12151             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12152         }
12153       }
12154
12155     // ----------------------------------
12156     // 3. Copy present boundary elements
12157     // ----------------------------------
12158     if ( toCopyExistingBoundary )
12159       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12160       {
12161         const SMDS_MeshElement* e = presentBndElems[i];
12162         TConnectivity nodes( e->NbNodes() );
12163         for ( inode = 0; inode < nodes.size(); ++inode )
12164           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12165         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12166       }
12167     else // store present elements to add them to a group
12168       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12169       {
12170         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12171       }
12172
12173   } // loop on given elements
12174
12175   // ---------------------------------------------
12176   // 4. Fill group with boundary elements
12177   // ---------------------------------------------
12178   if ( group )
12179   {
12180     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12181       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12182         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12183   }
12184   tgtEditor.myLastCreatedElems.Clear();
12185   tgtEditor2.myLastCreatedElems.Clear();
12186
12187   // -----------------------
12188   // 5. Copy given elements
12189   // -----------------------
12190   if ( toCopyElements && targetMesh != myMesh )
12191   {
12192     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12193     else                  eIt = elemSetIterator( elements );
12194     while (eIt->more())
12195     {
12196       const SMDS_MeshElement* elem = eIt->next();
12197       TConnectivity nodes( elem->NbNodes() );
12198       for ( inode = 0; inode < nodes.size(); ++inode )
12199         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12200       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12201
12202       tgtEditor.myLastCreatedElems.Clear();
12203     }
12204   }
12205   return nbAddedBnd;
12206 }