1 // Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
168 else if ( myType == SMDSAbs_Ball && !basicOnly )
170 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
176 //=======================================================================
180 //=======================================================================
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184 const ElemFeatures& features)
186 SMDS_MeshElement* e = 0;
187 int nbnode = node.size();
188 SMESHDS_Mesh* mesh = GetMeshDS();
189 const int ID = features.myID;
191 switch ( features.myType ) {
193 if ( !features.myIsPoly ) {
195 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196 else e = mesh->AddFace (node[0], node[1], node[2] );
198 else if (nbnode == 4) {
199 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
208 else if (nbnode == 7) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6] );
214 else if (nbnode == 8) {
215 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7], ID);
217 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7] );
220 else if (nbnode == 9) {
221 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7], node[8], ID);
223 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8] );
227 else if ( !features.myIsQuad )
229 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230 else e = mesh->AddPolygonalFace (node );
232 else if ( nbnode % 2 == 0 ) // just a protection
234 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235 else e = mesh->AddQuadPolygonalFace (node );
240 if ( !features.myIsPoly ) {
242 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
245 else if (nbnode == 5) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
251 else if (nbnode == 6) {
252 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253 node[4], node[5], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 else if (nbnode == 8) {
258 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 node[4], node[5], node[6], node[7], ID);
260 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7] );
263 else if (nbnode == 10) {
264 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7],
271 else if (nbnode == 12) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10], node[11], ID);
275 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], node[10], node[11] );
279 else if (nbnode == 13) {
280 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
282 node[8], node[9], node[10],node[11],
284 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10],node[11],
289 else if (nbnode == 15) {
290 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291 node[4], node[5], node[6], node[7],
292 node[8], node[9], node[10],node[11],
293 node[12],node[13],node[14],ID);
294 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
297 node[12],node[13],node[14] );
299 else if (nbnode == 18) {
300 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301 node[4], node[5], node[6], node[7],
302 node[8], node[9], node[10],node[11],
303 node[12],node[13],node[14],
304 node[15],node[16],node[17],ID );
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14],
309 node[15],node[16],node[17] );
311 else if (nbnode == 20) {
312 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313 node[4], node[5], node[6], node[7],
314 node[8], node[9], node[10],node[11],
315 node[12],node[13],node[14],node[15],
316 node[16],node[17],node[18],node[19],ID);
317 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
318 node[4], node[5], node[6], node[7],
319 node[8], node[9], node[10],node[11],
320 node[12],node[13],node[14],node[15],
321 node[16],node[17],node[18],node[19] );
323 else if (nbnode == 27) {
324 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325 node[4], node[5], node[6], node[7],
326 node[8], node[9], node[10],node[11],
327 node[12],node[13],node[14],node[15],
328 node[16],node[17],node[18],node[19],
329 node[20],node[21],node[22],node[23],
330 node[24],node[25],node[26], ID);
331 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
332 node[4], node[5], node[6], node[7],
333 node[8], node[9], node[10],node[11],
334 node[12],node[13],node[14],node[15],
335 node[16],node[17],node[18],node[19],
336 node[20],node[21],node[22],node[23],
337 node[24],node[25],node[26] );
340 else if ( !features.myIsQuad )
342 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
347 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
354 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355 else e = mesh->AddEdge (node[0], node[1] );
357 else if ( nbnode == 3 ) {
358 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359 else e = mesh->AddEdge (node[0], node[1], node[2] );
363 case SMDSAbs_0DElement:
365 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366 else e = mesh->Add0DElement (node[0] );
371 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
376 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377 else e = mesh->AddBall (node[0], features.myBallDiameter );
382 if ( e ) myLastCreatedElems.push_back( e );
386 //=======================================================================
390 //=======================================================================
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393 const ElemFeatures& features)
395 vector<const SMDS_MeshNode*> nodes;
396 nodes.reserve( nodeIDs.size() );
397 vector<int>::const_iterator id = nodeIDs.begin();
398 while ( id != nodeIDs.end() ) {
399 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400 nodes.push_back( node );
404 return AddElement( nodes, features );
407 //=======================================================================
409 //purpose : Remove a node or an element.
410 // Modify a compute state of sub-meshes which become empty
411 //=======================================================================
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object.
478 * \param elements - Elements on whose nodes to create 0D elements; if empty,
479 * the all mesh is treated
480 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481 * \param duplicateElements - to add one more 0D element to a node or not
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems,
487 const bool duplicateElements )
489 SMDS_ElemIteratorPtr elemIt;
490 if ( elements.empty() )
492 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496 elemIt = SMESHUtils::elemSetIterator( elements );
499 while ( elemIt->more() )
501 const SMDS_MeshElement* e = elemIt->next();
502 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503 while ( nodeIt->more() )
505 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507 if ( duplicateElements || !it0D->more() )
509 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510 all0DElems.insert( myLastCreatedElems.back() );
512 while ( it0D->more() )
513 all0DElems.insert( it0D->next() );
518 //=======================================================================
519 //function : FindShape
520 //purpose : Return an index of the shape theElem is on
521 // or zero if a shape not found
522 //=======================================================================
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
528 SMESHDS_Mesh * aMesh = GetMeshDS();
529 if ( aMesh->ShapeToMesh().IsNull() )
532 int aShapeID = theElem->getshapeId();
536 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537 if ( sm->Contains( theElem ))
540 if ( theElem->GetType() == SMDSAbs_Node ) {
541 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547 TopoDS_Shape aShape; // the shape a node of theElem is on
548 if ( theElem->GetType() != SMDSAbs_Node )
550 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551 while ( nodeIt->more() ) {
552 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553 if ((aShapeID = node->getshapeId()) > 0) {
554 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555 if ( sm->Contains( theElem ))
557 if ( aShape.IsNull() )
558 aShape = aMesh->IndexToShape( aShapeID );
564 // None of nodes is on a proper shape,
565 // find the shape among ancestors of aShape on which a node is
566 if ( !aShape.IsNull() ) {
567 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568 for ( ; ancIt.More(); ancIt.Next() ) {
569 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570 if ( sm && sm->Contains( theElem ))
571 return aMesh->ShapeToIndex( ancIt.Value() );
576 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577 while ( const SMESHDS_SubMesh* sm = smIt->next() )
578 if ( sm->Contains( theElem ))
585 //=======================================================================
586 //function : IsMedium
588 //=======================================================================
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
591 const SMDSAbs_ElementType typeToCheck)
593 bool isMedium = false;
594 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595 while (it->more() && !isMedium ) {
596 const SMDS_MeshElement* elem = it->next();
597 isMedium = elem->IsMediumNode(node);
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose : Shift nodes in the array corresponded to quadratic triangle
605 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 const SMDS_MeshNode* nd1 = aNodes[0];
611 aNodes[0] = aNodes[1];
612 aNodes[1] = aNodes[2];
614 const SMDS_MeshNode* nd2 = aNodes[3];
615 aNodes[3] = aNodes[4];
616 aNodes[4] = aNodes[5];
620 //=======================================================================
621 //function : getNodesFromTwoTria
623 //=======================================================================
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626 const SMDS_MeshElement * theTria2,
627 vector< const SMDS_MeshNode*>& N1,
628 vector< const SMDS_MeshNode*>& N2)
630 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631 if ( N1.size() < 6 ) return false;
632 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633 if ( N2.size() < 6 ) return false;
635 int sames[3] = {-1,-1,-1};
647 if(nbsames!=2) return false;
649 shiftNodesQuadTria(N1);
651 shiftNodesQuadTria(N1);
654 i = sames[0] + sames[1] + sames[2];
656 shiftNodesQuadTria(N2);
658 // now we receive following N1 and N2 (using numeration as in the image below)
659 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
660 // i.e. first nodes from both arrays form a new diagonal
664 //=======================================================================
665 //function : InverseDiag
666 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
667 // but having other common link.
668 // Return False if args are improper
669 //=======================================================================
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672 const SMDS_MeshElement * theTria2 )
676 if ( !theTria1 || !theTria2 ||
677 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679 theTria1->GetType() != SMDSAbs_Face ||
680 theTria2->GetType() != SMDSAbs_Face )
683 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684 (theTria2->GetEntityType() == SMDSEntity_Triangle))
686 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
691 // put nodes in array and find out indices of the same ones
692 const SMDS_MeshNode* aNodes [6];
693 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
695 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696 while ( it->more() ) {
697 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
699 if ( i > 2 ) // theTria2
700 // find same node of theTria1
701 for ( int j = 0; j < 3; j++ )
702 if ( aNodes[ i ] == aNodes[ j ]) {
711 return false; // theTria1 is not a triangle
712 it = theTria2->nodesIterator();
714 if ( i == 6 && it->more() )
715 return false; // theTria2 is not a triangle
718 // find indices of 1,2 and of A,B in theTria1
719 int iA = -1, iB = 0, i1 = 0, i2 = 0;
720 for ( i = 0; i < 6; i++ ) {
721 if ( sameInd [ i ] == -1 ) {
726 if ( iA >= 0) iB = i;
730 // nodes 1 and 2 should not be the same
731 if ( aNodes[ i1 ] == aNodes[ i2 ] )
735 aNodes[ iA ] = aNodes[ i2 ];
737 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
739 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
744 } // end if(F1 && F2)
746 // check case of quadratic faces
747 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
750 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
755 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
764 vector< const SMDS_MeshNode* > N1;
765 vector< const SMDS_MeshNode* > N2;
766 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
768 // now we receive following N1 and N2 (using numeration as above image)
769 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
770 // i.e. first nodes from both arrays determ new diagonal
772 vector< const SMDS_MeshNode*> N1new( N1.size() );
773 vector< const SMDS_MeshNode*> N2new( N2.size() );
774 N1new.back() = N1.back(); // central node of biquadratic
775 N2new.back() = N2.back();
776 N1new[0] = N1[0]; N2new[0] = N1[0];
777 N1new[1] = N2[0]; N2new[1] = N1[1];
778 N1new[2] = N2[1]; N2new[2] = N2[0];
779 N1new[3] = N1[4]; N2new[3] = N1[3];
780 N1new[4] = N2[3]; N2new[4] = N2[5];
781 N1new[5] = N1[5]; N2new[5] = N1[4];
782 // change nodes in faces
783 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
786 // move the central node of biquadratic triangle
787 SMESH_MesherHelper helper( *GetMesh() );
788 for ( int is2nd = 0; is2nd < 2; ++is2nd )
790 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
791 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792 if ( nodes.size() < 7 )
794 helper.SetSubShape( tria->getshapeId() );
795 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
799 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800 SMESH_NodeXYZ( nodes[4] ) +
801 SMESH_NodeXYZ( nodes[5] )) / 3.;
806 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
810 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811 xyz = S->Value( uv.X(), uv.Y() );
812 xyz.Transform( loc );
813 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
814 nodes[6]->getshapeId() > 0 )
815 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
817 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
822 //=======================================================================
823 //function : findTriangles
824 //purpose : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
827 static bool findTriangles(const SMDS_MeshNode * theNode1,
828 const SMDS_MeshNode * theNode2,
829 const SMDS_MeshElement*& theTria1,
830 const SMDS_MeshElement*& theTria2)
832 if ( !theNode1 || !theNode2 ) return false;
834 theTria1 = theTria2 = 0;
836 set< const SMDS_MeshElement* > emap;
837 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
839 const SMDS_MeshElement* elem = it->next();
840 if ( elem->NbCornerNodes() == 3 )
843 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
845 const SMDS_MeshElement* elem = it->next();
846 if ( emap.count( elem )) {
854 // theTria1 must be element with minimum ID
855 if ( theTria2->GetID() < theTria1->GetID() )
856 std::swap( theTria2, theTria1 );
864 //=======================================================================
865 //function : InverseDiag
866 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
867 // with ones built on the same 4 nodes but having other common link.
868 // Return false if proper faces not found
869 //=======================================================================
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872 const SMDS_MeshNode * theNode2)
876 const SMDS_MeshElement *tr1, *tr2;
877 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
880 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
884 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
887 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
892 // put nodes in array
893 // and find indices of 1,2 and of A in tr1 and of B in tr2
894 int i, iA1 = 0, i1 = 0;
895 const SMDS_MeshNode* aNodes1 [3];
896 SMDS_ElemIteratorPtr it;
897 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899 if ( aNodes1[ i ] == theNode1 )
900 iA1 = i; // node A in tr1
901 else if ( aNodes1[ i ] != theNode2 )
905 const SMDS_MeshNode* aNodes2 [3];
906 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes2[ i ] == theNode2 )
909 iB2 = i; // node B in tr2
910 else if ( aNodes2[ i ] != theNode1 )
914 // nodes 1 and 2 should not be the same
915 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
919 aNodes1[ iA1 ] = aNodes2[ i2 ];
921 aNodes2[ iB2 ] = aNodes1[ i1 ];
923 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
929 // check case of quadratic faces
930 return InverseDiag(tr1,tr2);
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
936 // fusion of triangles tr1 and tr2 having shared link on
937 // theNode1 and theNode2
938 //=======================================================================
940 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
941 const SMDS_MeshNode * theNode1,
942 const SMDS_MeshNode * theNode2,
943 const SMDS_MeshElement * tr1,
944 const SMDS_MeshElement * tr2 )
946 if( tr1->NbNodes() != tr2->NbNodes() )
948 // find the 4-th node to insert into tr1
949 const SMDS_MeshNode* n4 = 0;
950 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
952 while ( !n4 && i<3 ) {
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
959 // Make an array of nodes to be in a quadrangle
960 int iNode = 0, iFirstDiag = -1;
961 it = tr1->nodesIterator();
964 const SMDS_MeshNode * n = cast2Node( it->next() );
966 bool isDiag = ( n == theNode1 || n == theNode2 );
968 if ( iFirstDiag < 0 )
970 else if ( iNode - iFirstDiag == 1 )
971 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
973 else if ( n == n4 ) {
974 return false; // tr1 and tr2 should not have all the same nodes
976 theQuadNodes[ iNode++ ] = n;
978 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979 theQuadNodes[ iNode ] = n4;
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
987 // with a quadrangle built on the same 4 nodes.
988 // Return false if proper faces not found
989 //=======================================================================
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992 const SMDS_MeshNode * theNode2)
996 const SMDS_MeshElement *tr1, *tr2;
997 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1000 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1004 SMESHDS_Mesh * aMesh = GetMeshDS();
1006 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007 (tr2->GetEntityType() == SMDSEntity_Triangle))
1009 const SMDS_MeshNode* aNodes [ 4 ];
1010 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1013 const SMDS_MeshElement* newElem = 0;
1014 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015 myLastCreatedElems.push_back(newElem);
1016 AddToSameGroups( newElem, tr1, aMesh );
1017 int aShapeId = tr1->getshapeId();
1019 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1021 aMesh->RemoveElement( tr1 );
1022 aMesh->RemoveElement( tr2 );
1027 // check case of quadratic faces
1028 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1030 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1034 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1043 vector< const SMDS_MeshNode* > N1;
1044 vector< const SMDS_MeshNode* > N2;
1045 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1047 // now we receive following N1 and N2 (using numeration as above image)
1048 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1049 // i.e. first nodes from both arrays determ new diagonal
1051 const SMDS_MeshNode* aNodes[8];
1061 const SMDS_MeshElement* newElem = 0;
1062 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064 myLastCreatedElems.push_back(newElem);
1065 AddToSameGroups( newElem, tr1, aMesh );
1066 int aShapeId = tr1->getshapeId();
1069 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1071 aMesh->RemoveElement( tr1 );
1072 aMesh->RemoveElement( tr2 );
1074 // remove middle node (9)
1075 GetMeshDS()->RemoveNode( N1[4] );
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose : Reverse theElement orientation
1083 //=======================================================================
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1091 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092 if ( !it || !it->more() )
1095 const SMDSAbs_ElementType type = theElem->GetType();
1096 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1099 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1102 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1104 MESSAGE("Warning: bad volumic element");
1107 SMDS_VolumeTool vTool( aPolyedre );
1108 const int nbFaces = vTool.NbFaces();
1109 vector<int> quantities( nbFaces );
1110 vector<const SMDS_MeshNode *> poly_nodes;
1112 // check if all facets are oriented equally
1113 bool sameOri = true;
1114 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115 for (int iface = 0; iface < nbFaces; iface++)
1117 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118 if ( facetOri[ iface ] != facetOri[ 0 ])
1122 // reverse faces of the polyhedron
1123 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124 poly_nodes.reserve( vTool.NbNodes() );
1125 for ( int iface = 0; iface < nbFaces; iface++ )
1127 int nbFaceNodes = vTool.NbFaceNodes( iface );
1128 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129 bool toReverse = ( facetOri[ iface ] != neededOri );
1131 quantities[ iface ] = nbFaceNodes;
1134 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135 poly_nodes.push_back( nodes[ inode ]);
1137 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1139 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1141 else // other elements
1143 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145 if ( interlace.empty() )
1147 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1151 SMDS_MeshCell::applyInterlace( interlace, nodes );
1153 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1158 //================================================================================
1160 * \brief Reorient faces.
1161 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162 * \param theDirection - desired direction of normal of \a theFace
1163 * \param theFace - one of \a theFaces that should be oriented according to
1164 * \a theDirection and whose orientation defines orientation of other faces
1165 * \return number of reoriented faces.
1167 //================================================================================
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1170 const gp_Dir& theDirection,
1171 const SMDS_MeshElement * theFace)
1174 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1176 if ( theFaces.empty() )
1178 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179 while ( fIt->more() )
1180 theFaces.insert( theFaces.end(), fIt->next() );
1183 // orient theFace according to theDirection
1185 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186 if ( normal * theDirection.XYZ() < 0 )
1187 nbReori += Reorient( theFace );
1189 // Orient other faces
1191 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192 TIDSortedElemSet avoidSet;
1193 set< SMESH_TLink > checkedLinks;
1194 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1196 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197 theFaces.erase( theFace );
1198 startFaces.insert( theFace );
1200 int nodeInd1, nodeInd2;
1201 const SMDS_MeshElement* otherFace;
1202 vector< const SMDS_MeshElement* > facesNearLink;
1203 vector< std::pair< int, int > > nodeIndsOfFace;
1205 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206 while ( !startFaces.empty() )
1208 startFace = startFaces.begin();
1209 theFace = *startFace;
1210 startFaces.erase( startFace );
1211 if ( !visitedFaces.insert( theFace ).second )
1215 avoidSet.insert(theFace);
1217 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1219 const int nbNodes = theFace->NbCornerNodes();
1220 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1222 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223 linkIt_isNew = checkedLinks.insert( link );
1224 if ( !linkIt_isNew.second )
1226 // link has already been checked and won't be encountered more
1227 // if the group (theFaces) is manifold
1228 //checkedLinks.erase( linkIt_isNew.first );
1232 facesNearLink.clear();
1233 nodeIndsOfFace.clear();
1234 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1236 &nodeInd1, &nodeInd2 )))
1237 if ( otherFace != theFace)
1239 facesNearLink.push_back( otherFace );
1240 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241 avoidSet.insert( otherFace );
1243 if ( facesNearLink.size() > 1 )
1245 // NON-MANIFOLD mesh shell !
1246 // select a face most co-directed with theFace,
1247 // other faces won't be visited this time
1249 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250 double proj, maxProj = -1;
1251 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253 if (( proj = Abs( NF * NOF )) > maxProj ) {
1255 otherFace = facesNearLink[i];
1256 nodeInd1 = nodeIndsOfFace[i].first;
1257 nodeInd2 = nodeIndsOfFace[i].second;
1260 // not to visit rejected faces
1261 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263 visitedFaces.insert( facesNearLink[i] );
1265 else if ( facesNearLink.size() == 1 )
1267 otherFace = facesNearLink[0];
1268 nodeInd1 = nodeIndsOfFace.back().first;
1269 nodeInd2 = nodeIndsOfFace.back().second;
1271 if ( otherFace && otherFace != theFace)
1273 // link must be reverse in otherFace if orientation to otherFace
1274 // is same as that of theFace
1275 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1277 nbReori += Reorient( otherFace );
1279 startFaces.insert( otherFace );
1282 std::swap( link.first, link.second ); // reverse the link
1288 //================================================================================
1290 * \brief Reorient faces basing on orientation of adjacent volumes.
1291 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292 * \param theVolumes - reference volumes.
1293 * \param theOutsideNormal - to orient faces to have their normal
1294 * pointing either \a outside or \a inside the adjacent volumes.
1295 * \return number of reoriented faces.
1297 //================================================================================
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300 TIDSortedElemSet & theVolumes,
1301 const bool theOutsideNormal)
1305 SMDS_ElemIteratorPtr faceIt;
1306 if ( theFaces.empty() )
1307 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1309 faceIt = SMESHUtils::elemSetIterator( theFaces );
1311 vector< const SMDS_MeshNode* > faceNodes;
1312 TIDSortedElemSet checkedVolumes;
1313 set< const SMDS_MeshNode* > faceNodesSet;
1314 SMDS_VolumeTool volumeTool;
1316 while ( faceIt->more() ) // loop on given faces
1318 const SMDS_MeshElement* face = faceIt->next();
1319 if ( face->GetType() != SMDSAbs_Face )
1322 const size_t nbCornersNodes = face->NbCornerNodes();
1323 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1325 checkedVolumes.clear();
1326 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327 while ( vIt->more() )
1329 const SMDS_MeshElement* volume = vIt->next();
1331 if ( !checkedVolumes.insert( volume ).second )
1333 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336 // is volume adjacent?
1337 bool allNodesCommon = true;
1338 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340 if ( !allNodesCommon )
1343 // get nodes of a corresponding volume facet
1344 faceNodesSet.clear();
1345 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346 volumeTool.Set( volume );
1347 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348 if ( facetID < 0 ) continue;
1349 volumeTool.SetExternalNormal();
1350 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1352 // compare order of faceNodes and facetNodes
1353 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1355 for ( int i = 0; i < 2; ++i )
1357 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359 if ( faceNodes[ iN ] == n )
1365 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366 if ( isOutside != theOutsideNormal )
1367 nbReori += Reorient( face );
1369 } // loop on given faces
1374 //=======================================================================
1375 //function : getBadRate
1377 //=======================================================================
1379 static double getBadRate (const SMDS_MeshElement* theElem,
1380 SMESH::Controls::NumericalFunctorPtr& theCrit)
1382 SMESH::Controls::TSequenceOfXYZ P;
1383 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1385 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose : Cut quadrangles into triangles.
1392 // theCrit is used to select a diagonal to cut
1393 //=======================================================================
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1396 SMESH::Controls::NumericalFunctorPtr theCrit)
1400 if ( !theCrit.get() )
1403 SMESHDS_Mesh * aMesh = GetMeshDS();
1404 Handle(Geom_Surface) surface;
1405 SMESH_MesherHelper helper( *GetMesh() );
1407 myLastCreatedElems.reserve( theElems.size() * 2 );
1409 TIDSortedElemSet::iterator itElem;
1410 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1412 const SMDS_MeshElement* elem = *itElem;
1413 if ( !elem || elem->GetType() != SMDSAbs_Face )
1415 if ( elem->NbCornerNodes() != 4 )
1418 // retrieve element nodes
1419 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1421 // compare two sets of possible triangles
1422 double aBadRate1, aBadRate2; // to what extent a set is bad
1423 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1427 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1431 const int aShapeId = FindShape( elem );
1432 const SMDS_MeshElement* newElem1 = 0;
1433 const SMDS_MeshElement* newElem2 = 0;
1435 if ( !elem->IsQuadratic() ) // split linear quadrangle
1437 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439 if ( aBadRate1 <= aBadRate2 ) {
1440 // tr1 + tr2 is better
1441 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445 // tr3 + tr4 is better
1446 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450 else // split quadratic quadrangle
1452 helper.SetIsQuadratic( true );
1453 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1455 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456 if ( aNodes.size() == 9 )
1458 helper.SetIsBiQuadratic( true );
1459 if ( aBadRate1 <= aBadRate2 )
1460 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1462 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1464 // create a new element
1465 if ( aBadRate1 <= aBadRate2 ) {
1466 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1475 // care of a new element
1477 myLastCreatedElems.push_back(newElem1);
1478 myLastCreatedElems.push_back(newElem2);
1479 AddToSameGroups( newElem1, elem, aMesh );
1480 AddToSameGroups( newElem2, elem, aMesh );
1482 // put a new triangle on the same shape
1484 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1487 aMesh->RemoveElement( elem );
1492 //=======================================================================
1494 * \brief Split each of given quadrangles into 4 triangles.
1495 * \param theElems - The faces to be split. If empty all faces are split.
1497 //=======================================================================
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1502 myLastCreatedElems.reserve( theElems.size() * 4 );
1504 SMESH_MesherHelper helper( *GetMesh() );
1505 helper.SetElementsOnShape( true );
1507 // get standalone groups of faces
1508 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1509 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1510 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1511 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1512 allFaceGroups.push_back( & group->SMDSGroup() );
1515 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1517 vector< const SMDS_MeshNode* > nodes;
1518 SMESHDS_SubMesh* subMeshDS = 0;
1520 Handle(Geom_Surface) surface;
1521 TopLoc_Location loc;
1523 SMDS_ElemIteratorPtr faceIt;
1524 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1525 else faceIt = SMESHUtils::elemSetIterator( theElems );
1527 while ( faceIt->more() )
1529 const SMDS_MeshElement* quad = faceIt->next();
1530 if ( !quad || quad->NbCornerNodes() != 4 )
1533 // get a surface the quad is on
1535 if ( quad->getshapeId() < 1 )
1538 helper.SetSubShape( 0 );
1541 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543 helper.SetSubShape( quad->getshapeId() );
1544 if ( !helper.GetSubShape().IsNull() &&
1545 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547 F = TopoDS::Face( helper.GetSubShape() );
1548 surface = BRep_Tool::Surface( F, loc );
1549 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1553 helper.SetSubShape( 0 );
1558 // create a central node
1560 const SMDS_MeshNode* nCentral;
1561 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563 if ( nodes.size() == 9 )
1565 nCentral = nodes.back();
1572 for ( ; iN < nodes.size(); ++iN )
1573 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1575 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579 xyz[0], xyz[1], xyz[2], xyz[3],
1580 xyz[4], xyz[5], xyz[6], xyz[7] );
1584 for ( ; iN < nodes.size(); ++iN )
1585 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591 uv[0], uv[1], uv[2], uv[3],
1592 uv[4], uv[5], uv[6], uv[7] );
1594 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1598 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599 uv[8].X(), uv[8].Y() );
1600 myLastCreatedNodes.push_back( nCentral );
1603 helper.SetIsQuadratic ( nodes.size() > 4 );
1604 helper.SetIsBiQuadratic( nodes.size() == 9 );
1605 if ( helper.GetIsQuadratic() )
1606 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1608 // select groups to update
1610 for ( SMDS_MeshGroup* group : allFaceGroups )
1611 if ( group->Remove( quad ))
1612 faceGroups.push_back( group );
1614 // create 4 triangles
1616 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1618 for ( int i = 0; i < 4; ++i )
1620 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1623 myLastCreatedElems.push_back( tria );
1624 for ( SMDS_MeshGroup* group : faceGroups )
1630 //=======================================================================
1631 //function : BestSplit
1632 //purpose : Find better diagonal for cutting.
1633 //=======================================================================
1635 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1636 SMESH::Controls::NumericalFunctorPtr theCrit)
1643 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1646 if( theQuad->NbNodes()==4 ||
1647 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1649 // retrieve element nodes
1650 const SMDS_MeshNode* aNodes [4];
1651 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1653 //while (itN->more())
1655 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1657 // compare two sets of possible triangles
1658 double aBadRate1, aBadRate2; // to what extent a set is bad
1659 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1660 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1661 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1663 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1664 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1665 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1666 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1667 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1668 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1669 return 1; // diagonal 1-3
1671 return 2; // diagonal 2-4
1678 // Methods of splitting volumes into tetra
1680 const int theHexTo5_1[5*4+1] =
1682 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1684 const int theHexTo5_2[5*4+1] =
1686 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1688 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1690 const int theHexTo6_1[6*4+1] =
1692 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
1694 const int theHexTo6_2[6*4+1] =
1696 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
1698 const int theHexTo6_3[6*4+1] =
1700 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
1702 const int theHexTo6_4[6*4+1] =
1704 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
1706 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1708 const int thePyraTo2_1[2*4+1] =
1710 0, 1, 2, 4, 0, 2, 3, 4, -1
1712 const int thePyraTo2_2[2*4+1] =
1714 1, 2, 3, 4, 1, 3, 0, 4, -1
1716 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1718 const int thePentaTo3_1[3*4+1] =
1720 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1722 const int thePentaTo3_2[3*4+1] =
1724 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1726 const int thePentaTo3_3[3*4+1] =
1728 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1730 const int thePentaTo3_4[3*4+1] =
1732 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1734 const int thePentaTo3_5[3*4+1] =
1736 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1738 const int thePentaTo3_6[3*4+1] =
1740 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1742 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1743 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1745 // Methods of splitting hexahedron into prisms
1747 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1749 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
1751 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1753 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
1755 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1757 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
1760 const int theHexTo2Prisms_BT_1[6*2+1] =
1762 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1764 const int theHexTo2Prisms_BT_2[6*2+1] =
1766 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1768 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1770 const int theHexTo2Prisms_LR_1[6*2+1] =
1772 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1774 const int theHexTo2Prisms_LR_2[6*2+1] =
1776 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1778 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1780 const int theHexTo2Prisms_FB_1[6*2+1] =
1782 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1784 const int theHexTo2Prisms_FB_2[6*2+1] =
1786 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1788 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1791 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1794 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1795 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1796 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1797 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1803 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1804 bool _baryNode; //!< additional node is to be created at cell barycenter
1805 bool _ownConn; //!< to delete _connectivity in destructor
1806 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1808 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1809 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1810 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1811 bool hasFacet( const TTriangleFacet& facet ) const
1813 if ( _nbCorners == 4 )
1815 const int* tetConn = _connectivity;
1816 for ( ; tetConn[0] >= 0; tetConn += 4 )
1817 if (( facet.contains( tetConn[0] ) +
1818 facet.contains( tetConn[1] ) +
1819 facet.contains( tetConn[2] ) +
1820 facet.contains( tetConn[3] )) == 3 )
1823 else // prism, _nbCorners == 6
1825 const int* prismConn = _connectivity;
1826 for ( ; prismConn[0] >= 0; prismConn += 6 )
1828 if (( facet.contains( prismConn[0] ) &&
1829 facet.contains( prismConn[1] ) &&
1830 facet.contains( prismConn[2] ))
1832 ( facet.contains( prismConn[3] ) &&
1833 facet.contains( prismConn[4] ) &&
1834 facet.contains( prismConn[5] )))
1842 //=======================================================================
1844 * \brief return TSplitMethod for the given element to split into tetrahedra
1846 //=======================================================================
1848 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1850 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1852 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1853 // an edge and a face barycenter; tertaherdons are based on triangles and
1854 // a volume barycenter
1855 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1857 // Find out how adjacent volumes are split
1859 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1860 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1861 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1863 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1864 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1865 if ( nbNodes < 4 ) continue;
1867 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1868 const int* nInd = vol.GetFaceNodesIndices( iF );
1871 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1872 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1873 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1874 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1878 int iCom = 0; // common node of triangle faces to split into
1879 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1881 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1882 nInd[ iQ * ( (iCom+1)%nbNodes )],
1883 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1884 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1885 nInd[ iQ * ( (iCom+2)%nbNodes )],
1886 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1887 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1889 triaSplits.push_back( t012 );
1890 triaSplits.push_back( t023 );
1895 if ( !triaSplits.empty() )
1896 hasAdjacentSplits = true;
1899 // Among variants of split method select one compliant with adjacent volumes
1901 TSplitMethod method;
1902 if ( !vol.Element()->IsPoly() && !is24TetMode )
1904 int nbVariants = 2, nbTet = 0;
1905 const int** connVariants = 0;
1906 switch ( vol.Element()->GetEntityType() )
1908 case SMDSEntity_Hexa:
1909 case SMDSEntity_Quad_Hexa:
1910 case SMDSEntity_TriQuad_Hexa:
1911 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1912 connVariants = theHexTo5, nbTet = 5;
1914 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1916 case SMDSEntity_Pyramid:
1917 case SMDSEntity_Quad_Pyramid:
1918 connVariants = thePyraTo2; nbTet = 2;
1920 case SMDSEntity_Penta:
1921 case SMDSEntity_Quad_Penta:
1922 case SMDSEntity_BiQuad_Penta:
1923 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1928 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1930 // check method compliancy with adjacent tetras,
1931 // all found splits must be among facets of tetras described by this method
1932 method = TSplitMethod( nbTet, connVariants[variant] );
1933 if ( hasAdjacentSplits && method._nbSplits > 0 )
1935 bool facetCreated = true;
1936 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1938 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1939 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1940 facetCreated = method.hasFacet( *facet );
1942 if ( !facetCreated )
1943 method = TSplitMethod(0); // incompatible method
1947 if ( method._nbSplits < 1 )
1949 // No standard method is applicable, use a generic solution:
1950 // each facet of a volume is split into triangles and
1951 // each of triangles and a volume barycenter form a tetrahedron.
1953 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1955 int* connectivity = new int[ maxTetConnSize + 1 ];
1956 method._connectivity = connectivity;
1957 method._ownConn = true;
1958 method._baryNode = !isHex27; // to create central node or not
1961 int baryCenInd = vol.NbNodes() - int( isHex27 );
1962 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1964 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1965 const int* nInd = vol.GetFaceNodesIndices( iF );
1966 // find common node of triangle facets of tetra to create
1967 int iCommon = 0; // index in linear numeration
1968 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1969 if ( !triaSplits.empty() )
1972 const TTriangleFacet* facet = &triaSplits.front();
1973 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1974 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1975 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1978 else if ( nbNodes > 3 && !is24TetMode )
1980 // find the best method of splitting into triangles by aspect ratio
1981 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1982 map< double, int > badness2iCommon;
1983 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1984 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1985 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1988 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1990 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1991 nodes[ iQ*((iLast-1)%nbNodes)],
1992 nodes[ iQ*((iLast )%nbNodes)]);
1993 badness += getBadRate( &tria, aspectRatio );
1995 badness2iCommon.insert( make_pair( badness, iCommon ));
1997 // use iCommon with lowest badness
1998 iCommon = badness2iCommon.begin()->second;
2000 if ( iCommon >= nbNodes )
2001 iCommon = 0; // something wrong
2003 // fill connectivity of tetrahedra based on a current face
2004 int nbTet = nbNodes - 2;
2005 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2010 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2011 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2015 method._faceBaryNode[ iF ] = 0;
2016 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2019 for ( int i = 0; i < nbTet; ++i )
2021 int i1 = i, i2 = (i+1) % nbNodes;
2022 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2023 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2024 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2025 connectivity[ connSize++ ] = faceBaryCenInd;
2026 connectivity[ connSize++ ] = baryCenInd;
2031 for ( int i = 0; i < nbTet; ++i )
2033 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2034 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2035 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2036 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038 connectivity[ connSize++ ] = baryCenInd;
2041 method._nbSplits += nbTet;
2043 } // loop on volume faces
2045 connectivity[ connSize++ ] = -1;
2047 } // end of generic solution
2051 //=======================================================================
2053 * \brief return TSplitMethod to split haxhedron into prisms
2055 //=======================================================================
2057 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2058 const int methodFlags,
2059 const int facetToSplit)
2061 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2063 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2065 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2067 static TSplitMethod to4methods[4]; // order BT, LR, FB
2068 if ( to4methods[iF]._nbSplits == 0 )
2072 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2073 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2074 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2077 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2078 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2079 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2082 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2083 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2084 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2086 default: return to4methods[3];
2088 to4methods[iF]._nbSplits = 4;
2089 to4methods[iF]._nbCorners = 6;
2091 return to4methods[iF];
2093 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2095 TSplitMethod method;
2097 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2099 const int nbVariants = 2, nbSplits = 2;
2100 const int** connVariants = 0;
2102 case 0: connVariants = theHexTo2Prisms_BT; break;
2103 case 1: connVariants = theHexTo2Prisms_LR; break;
2104 case 2: connVariants = theHexTo2Prisms_FB; break;
2105 default: return method;
2108 // look for prisms adjacent via facetToSplit and an opposite one
2109 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2111 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2112 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2113 if ( nbNodes != 4 ) return method;
2115 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2116 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2117 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2119 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2121 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2126 // there are adjacent prism
2127 for ( int variant = 0; variant < nbVariants; ++variant )
2129 // check method compliancy with adjacent prisms,
2130 // the found prism facets must be among facets of prisms described by current method
2131 method._nbSplits = nbSplits;
2132 method._nbCorners = 6;
2133 method._connectivity = connVariants[ variant ];
2134 if ( method.hasFacet( *t ))
2139 // No adjacent prisms. Select a variant with a best aspect ratio.
2141 double badness[2] = { 0., 0. };
2142 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2143 const SMDS_MeshNode** nodes = vol.GetNodes();
2144 for ( int variant = 0; variant < nbVariants; ++variant )
2145 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2147 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2148 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2150 method._connectivity = connVariants[ variant ];
2151 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2152 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2153 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2155 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2158 badness[ variant ] += getBadRate( &tria, aspectRatio );
2160 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2162 method._nbSplits = nbSplits;
2163 method._nbCorners = 6;
2164 method._connectivity = connVariants[ iBetter ];
2169 //================================================================================
2171 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2173 //================================================================================
2175 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2176 const SMDSAbs_GeometryType geom ) const
2178 // find the tetrahedron including the three nodes of facet
2179 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2180 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2181 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2182 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2183 while ( volIt1->more() )
2185 const SMDS_MeshElement* v = volIt1->next();
2186 if ( v->GetGeomType() != geom )
2188 const int lastCornerInd = v->NbCornerNodes() - 1;
2189 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2190 continue; // medium node not allowed
2191 const int ind2 = v->GetNodeIndex( n2 );
2192 if ( ind2 < 0 || lastCornerInd < ind2 )
2194 const int ind3 = v->GetNodeIndex( n3 );
2195 if ( ind3 < 0 || lastCornerInd < ind3 )
2202 //=======================================================================
2204 * \brief A key of a face of volume
2206 //=======================================================================
2208 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2210 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2212 TIDSortedNodeSet sortedNodes;
2213 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2214 int nbNodes = vol.NbFaceNodes( iF );
2215 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2216 for ( int i = 0; i < nbNodes; i += iQ )
2217 sortedNodes.insert( fNodes[i] );
2218 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2219 first.first = (*(n++))->GetID();
2220 first.second = (*(n++))->GetID();
2221 second.first = (*(n++))->GetID();
2222 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2227 //=======================================================================
2228 //function : SplitVolumes
2229 //purpose : Split volume elements into tetrahedra or prisms.
2230 // If facet ID < 0, element is split into tetrahedra,
2231 // else a hexahedron is split into prisms so that the given facet is
2232 // split into triangles
2233 //=======================================================================
2235 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2236 const int theMethodFlags)
2238 SMDS_VolumeTool volTool;
2239 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2240 fHelper.ToFixNodeParameters( true );
2242 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2243 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2245 SMESH_SequenceOfElemPtr newNodes, newElems;
2247 // map face of volume to it's baricenrtic node
2248 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2250 vector<const SMDS_MeshElement* > splitVols;
2252 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2253 for ( ; elem2facet != theElems.end(); ++elem2facet )
2255 const SMDS_MeshElement* elem = elem2facet->first;
2256 const int facetToSplit = elem2facet->second;
2257 if ( elem->GetType() != SMDSAbs_Volume )
2259 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2260 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2263 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2265 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2266 getTetraSplitMethod( volTool, theMethodFlags ) :
2267 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2268 if ( splitMethod._nbSplits < 1 ) continue;
2270 // find submesh to add new tetras to
2271 if ( !subMesh || !subMesh->Contains( elem ))
2273 int shapeID = FindShape( elem );
2274 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2275 subMesh = GetMeshDS()->MeshElements( shapeID );
2278 if ( elem->IsQuadratic() )
2281 // add quadratic links to the helper
2282 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2284 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2285 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2286 for ( int iN = 0; iN < nbN; iN += iQ )
2287 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2289 helper.SetIsQuadratic( true );
2294 helper.SetIsQuadratic( false );
2296 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2297 volTool.GetNodes() + elem->NbNodes() );
2298 helper.SetElementsOnShape( true );
2299 if ( splitMethod._baryNode )
2301 // make a node at barycenter
2302 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2303 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2304 nodes.push_back( gcNode );
2305 newNodes.push_back( gcNode );
2307 if ( !splitMethod._faceBaryNode.empty() )
2309 // make or find baricentric nodes of faces
2310 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2311 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2313 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2314 volFace2BaryNode.insert
2315 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2318 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2319 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2321 nodes.push_back( iF_n->second = f_n->second );
2326 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2327 const int* volConn = splitMethod._connectivity;
2328 if ( splitMethod._nbCorners == 4 ) // tetra
2329 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331 nodes[ volConn[1] ],
2332 nodes[ volConn[2] ],
2333 nodes[ volConn[3] ]));
2335 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2336 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2337 nodes[ volConn[1] ],
2338 nodes[ volConn[2] ],
2339 nodes[ volConn[3] ],
2340 nodes[ volConn[4] ],
2341 nodes[ volConn[5] ]));
2343 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2345 // Split faces on sides of the split volume
2347 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2348 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2350 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2351 if ( nbNodes < 4 ) continue;
2353 // find an existing face
2354 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2355 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2356 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2357 /*noMedium=*/false))
2360 helper.SetElementsOnShape( false );
2361 vector< const SMDS_MeshElement* > triangles;
2363 // find submesh to add new triangles in
2364 if ( !fSubMesh || !fSubMesh->Contains( face ))
2366 int shapeID = FindShape( face );
2367 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2369 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2370 if ( iF_n != splitMethod._faceBaryNode.end() )
2372 const SMDS_MeshNode *baryNode = iF_n->second;
2373 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2375 const SMDS_MeshNode* n1 = fNodes[iN];
2376 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2377 const SMDS_MeshNode *n3 = baryNode;
2378 if ( !volTool.IsFaceExternal( iF ))
2380 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2382 if ( fSubMesh ) // update position of the bary node on geometry
2385 subMesh->RemoveNode( baryNode );
2386 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2387 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2388 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2390 fHelper.SetSubShape( s );
2391 gp_XY uv( 1e100, 1e100 );
2393 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2394 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2397 // node is too far from the surface
2398 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2399 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2400 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2407 // among possible triangles create ones described by split method
2408 const int* nInd = volTool.GetFaceNodesIndices( iF );
2409 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2410 int iCom = 0; // common node of triangle faces to split into
2411 list< TTriangleFacet > facets;
2412 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2414 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2415 nInd[ iQ * ( (iCom+1)%nbNodes )],
2416 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2417 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ( (iCom+2)%nbNodes )],
2419 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2420 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2422 facets.push_back( t012 );
2423 facets.push_back( t023 );
2424 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2425 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2426 nInd[ iQ * ((iLast-1)%nbNodes )],
2427 nInd[ iQ * ((iLast )%nbNodes )]));
2431 list< TTriangleFacet >::iterator facet = facets.begin();
2432 if ( facet == facets.end() )
2434 for ( ; facet != facets.end(); ++facet )
2436 if ( !volTool.IsFaceExternal( iF ))
2437 swap( facet->_n2, facet->_n3 );
2438 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2439 volNodes[ facet->_n2 ],
2440 volNodes[ facet->_n3 ]));
2443 for ( size_t i = 0; i < triangles.size(); ++i )
2445 if ( !triangles[ i ]) continue;
2447 fSubMesh->AddElement( triangles[ i ]);
2448 newElems.push_back( triangles[ i ]);
2450 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2451 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2453 } // while a face based on facet nodes exists
2454 } // loop on volume faces to split them into triangles
2456 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2458 if ( geomType == SMDSEntity_TriQuad_Hexa )
2460 // remove medium nodes that could become free
2461 for ( int i = 20; i < volTool.NbNodes(); ++i )
2462 if ( volNodes[i]->NbInverseElements() == 0 )
2463 GetMeshDS()->RemoveNode( volNodes[i] );
2465 } // loop on volumes to split
2467 myLastCreatedNodes = newNodes;
2468 myLastCreatedElems = newElems;
2471 //=======================================================================
2472 //function : GetHexaFacetsToSplit
2473 //purpose : For hexahedra that will be split into prisms, finds facets to
2474 // split into triangles. Only hexahedra adjacent to the one closest
2475 // to theFacetNormal.Location() are returned.
2476 //param [in,out] theHexas - the hexahedra
2477 //param [in] theFacetNormal - facet normal
2478 //param [out] theFacets - the hexahedra and found facet IDs
2479 //=======================================================================
2481 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2482 const gp_Ax1& theFacetNormal,
2483 TFacetOfElem & theFacets)
2485 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2487 // Find a hexa closest to the location of theFacetNormal
2489 const SMDS_MeshElement* startHex;
2491 // get SMDS_ElemIteratorPtr on theHexas
2492 typedef const SMDS_MeshElement* TValue;
2493 typedef TIDSortedElemSet::iterator TSetIterator;
2494 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2495 typedef SMDS_MeshElement::GeomFilter TFilter;
2496 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2497 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2498 ( new TElemSetIter( theHexas.begin(),
2500 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2502 SMESH_ElementSearcher* searcher =
2503 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2505 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2510 throw SALOME_Exception( THIS_METHOD "startHex not found");
2513 // Select a facet of startHex by theFacetNormal
2515 SMDS_VolumeTool vTool( startHex );
2516 double norm[3], dot, maxDot = 0;
2518 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2519 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2521 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2529 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2531 // Fill theFacets starting from facetID of startHex
2533 // facets used for searching of volumes adjacent to already treated ones
2534 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2535 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2536 TFacetMap facetsToCheck;
2538 set<const SMDS_MeshNode*> facetNodes;
2539 const SMDS_MeshElement* curHex;
2541 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2545 // move in two directions from startHex via facetID
2546 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2549 int curFacet = facetID;
2550 if ( is2nd ) // do not treat startHex twice
2552 vTool.Set( curHex );
2553 if ( vTool.IsFreeFace( curFacet, &curHex ))
2559 vTool.GetFaceNodes( curFacet, facetNodes );
2560 vTool.Set( curHex );
2561 curFacet = vTool.GetFaceIndex( facetNodes );
2566 // store a facet to split
2567 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2569 theFacets.insert( make_pair( curHex, -1 ));
2572 if ( !allHex && !theHexas.count( curHex ))
2575 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2576 theFacets.insert( make_pair( curHex, curFacet ));
2577 if ( !facetIt2isNew.second )
2580 // remember not-to-split facets in facetsToCheck
2581 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2582 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2584 if ( iF == curFacet && iF == oppFacet )
2586 TVolumeFaceKey facetKey ( vTool, iF );
2587 TElemFacets elemFacet( facetIt2isNew.first, iF );
2588 pair< TFacetMap::iterator, bool > it2isnew =
2589 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2590 if ( !it2isnew.second )
2591 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2593 // pass to a volume adjacent via oppFacet
2594 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2600 // get a new curFacet
2601 vTool.GetFaceNodes( oppFacet, facetNodes );
2602 vTool.Set( curHex );
2603 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2606 } // move in two directions from startHex via facetID
2608 // Find a new startHex by facetsToCheck
2612 TFacetMap::iterator fIt = facetsToCheck.begin();
2613 while ( !startHex && fIt != facetsToCheck.end() )
2615 const TElemFacets& elemFacets = fIt->second;
2616 const SMDS_MeshElement* hex = elemFacets.first->first;
2617 int splitFacet = elemFacets.first->second;
2618 int lateralFacet = elemFacets.second;
2619 facetsToCheck.erase( fIt );
2620 fIt = facetsToCheck.begin();
2623 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2624 curHex->GetGeomType() != SMDSGeom_HEXA )
2626 if ( !allHex && !theHexas.count( curHex ))
2631 // find a facet of startHex to split
2633 set<const SMDS_MeshNode*> lateralNodes;
2634 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2635 vTool.GetFaceNodes( splitFacet, facetNodes );
2636 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2637 vTool.Set( startHex );
2638 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2640 // look for a facet of startHex having common nodes with facetNodes
2641 // but not lateralFacet
2642 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2644 if ( iF == lateralFacet )
2646 int nbCommonNodes = 0;
2647 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2648 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2649 nbCommonNodes += facetNodes.count( nn[ iN ]);
2651 if ( nbCommonNodes >= 2 )
2658 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2660 } // while ( startHex )
2667 //================================================================================
2669 * \brief Selects nodes of several elements according to a given interlace
2670 * \param [in] srcNodes - nodes to select from
2671 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2672 * \param [in] interlace - indices of nodes for all elements
2673 * \param [in] nbElems - nb of elements
2674 * \param [in] nbNodes - nb of nodes in each element
2675 * \param [in] mesh - the mesh
2676 * \param [out] elemQueue - a list to push elements found by the selected nodes
2677 * \param [in] type - type of elements to look for
2679 //================================================================================
2681 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2682 vector< const SMDS_MeshNode* >* tgtNodesVec,
2683 const int* interlace,
2686 SMESHDS_Mesh* mesh = 0,
2687 list< const SMDS_MeshElement* >* elemQueue=0,
2688 SMDSAbs_ElementType type=SMDSAbs_All)
2690 for ( int iE = 0; iE < nbElems; ++iE )
2692 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2693 const int* select = & interlace[iE*nbNodes];
2694 elemNodes.resize( nbNodes );
2695 for ( int iN = 0; iN < nbNodes; ++iN )
2696 elemNodes[iN] = srcNodes[ select[ iN ]];
2698 const SMDS_MeshElement* e;
2700 for ( int iE = 0; iE < nbElems; ++iE )
2701 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2702 elemQueue->push_back( e );
2706 //=======================================================================
2708 * Split bi-quadratic elements into linear ones without creation of additional nodes
2709 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2710 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2711 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2712 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2713 * will be split in order to keep the mesh conformal.
2714 * \param elems - elements to split
2716 //=======================================================================
2718 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2720 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2721 vector<const SMDS_MeshElement* > splitElems;
2722 list< const SMDS_MeshElement* > elemQueue;
2723 list< const SMDS_MeshElement* >::iterator elemIt;
2725 SMESHDS_Mesh * mesh = GetMeshDS();
2726 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2727 int nbElems, nbNodes;
2729 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2730 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2733 elemQueue.push_back( *elemSetIt );
2734 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2736 const SMDS_MeshElement* elem = *elemIt;
2737 switch( elem->GetEntityType() )
2739 case SMDSEntity_TriQuad_Hexa: // HEX27
2741 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2742 nbElems = nbNodes = 8;
2743 elemType = & hexaType;
2745 // get nodes for new elements
2746 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2747 { 1,9,20,8, 17,22,26,21 },
2748 { 2,10,20,9, 18,23,26,22 },
2749 { 3,11,20,10, 19,24,26,23 },
2750 { 16,21,26,24, 4,12,25,15 },
2751 { 17,22,26,21, 5,13,25,12 },
2752 { 18,23,26,22, 6,14,25,13 },
2753 { 19,24,26,23, 7,15,25,14 }};
2754 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2756 // add boundary faces to elemQueue
2757 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2758 { 4,5,6,7, 12,13,14,15, 25 },
2759 { 0,1,5,4, 8,17,12,16, 21 },
2760 { 1,2,6,5, 9,18,13,17, 22 },
2761 { 2,3,7,6, 10,19,14,18, 23 },
2762 { 3,0,4,7, 11,16,15,19, 24 }};
2763 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2765 // add boundary segments to elemQueue
2766 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2767 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2768 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2769 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2772 case SMDSEntity_BiQuad_Triangle: // TRIA7
2774 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2777 elemType = & quadType;
2779 // get nodes for new elements
2780 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2781 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2783 // add boundary segments to elemQueue
2784 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2785 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2788 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2790 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2793 elemType = & quadType;
2795 // get nodes for new elements
2796 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2797 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2799 // add boundary segments to elemQueue
2800 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2801 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2804 case SMDSEntity_Quad_Edge:
2806 if ( elemIt == elemQueue.begin() )
2807 continue; // an elem is in theElems
2808 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2811 elemType = & segType;
2813 // get nodes for new elements
2814 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2815 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2819 } // switch( elem->GetEntityType() )
2821 // Create new elements
2823 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2827 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2828 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2829 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2830 //elemType->SetID( -1 );
2832 for ( int iE = 0; iE < nbElems; ++iE )
2833 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2836 ReplaceElemInGroups( elem, splitElems, mesh );
2839 for ( size_t i = 0; i < splitElems.size(); ++i )
2840 subMesh->AddElement( splitElems[i] );
2845 //=======================================================================
2846 //function : AddToSameGroups
2847 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2848 //=======================================================================
2850 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2851 const SMDS_MeshElement* elemInGroups,
2852 SMESHDS_Mesh * aMesh)
2854 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2855 if (!groups.empty()) {
2856 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2857 for ( ; grIt != groups.end(); grIt++ ) {
2858 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2859 if ( group && group->Contains( elemInGroups ))
2860 group->SMDSGroup().Add( elemToAdd );
2866 //=======================================================================
2867 //function : RemoveElemFromGroups
2868 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2869 //=======================================================================
2870 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2871 SMESHDS_Mesh * aMesh)
2873 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874 if (!groups.empty())
2876 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2877 for (; GrIt != groups.end(); GrIt++)
2879 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2880 if (!grp || grp->IsEmpty()) continue;
2881 grp->SMDSGroup().Remove(removeelem);
2886 //================================================================================
2888 * \brief Replace elemToRm by elemToAdd in the all groups
2890 //================================================================================
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2893 const SMDS_MeshElement* elemToAdd,
2894 SMESHDS_Mesh * aMesh)
2896 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897 if (!groups.empty()) {
2898 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2899 for ( ; grIt != groups.end(); grIt++ ) {
2900 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2901 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2902 group->SMDSGroup().Add( elemToAdd );
2907 //================================================================================
2909 * \brief Replace elemToRm by elemToAdd in the all groups
2911 //================================================================================
2913 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2914 const vector<const SMDS_MeshElement*>& elemToAdd,
2915 SMESHDS_Mesh * aMesh)
2917 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918 if (!groups.empty())
2920 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2921 for ( ; grIt != groups.end(); grIt++ ) {
2922 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2923 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2924 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2925 group->SMDSGroup().Add( elemToAdd[ i ] );
2930 //=======================================================================
2931 //function : QuadToTri
2932 //purpose : Cut quadrangles into triangles.
2933 // theCrit is used to select a diagonal to cut
2934 //=======================================================================
2936 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2937 const bool the13Diag)
2940 myLastCreatedElems.reserve( theElems.size() * 2 );
2942 SMESHDS_Mesh * aMesh = GetMeshDS();
2943 Handle(Geom_Surface) surface;
2944 SMESH_MesherHelper helper( *GetMesh() );
2946 TIDSortedElemSet::iterator itElem;
2947 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2949 const SMDS_MeshElement* elem = *itElem;
2950 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2953 if ( elem->NbNodes() == 4 ) {
2954 // retrieve element nodes
2955 const SMDS_MeshNode* aNodes [4];
2956 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2958 while ( itN->more() )
2959 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2961 int aShapeId = FindShape( elem );
2962 const SMDS_MeshElement* newElem1 = 0;
2963 const SMDS_MeshElement* newElem2 = 0;
2965 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2969 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2970 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2972 myLastCreatedElems.push_back(newElem1);
2973 myLastCreatedElems.push_back(newElem2);
2974 // put a new triangle on the same shape and add to the same groups
2977 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2978 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2980 AddToSameGroups( newElem1, elem, aMesh );
2981 AddToSameGroups( newElem2, elem, aMesh );
2982 aMesh->RemoveElement( elem );
2985 // Quadratic quadrangle
2987 else if ( elem->NbNodes() >= 8 )
2989 // get surface elem is on
2990 int aShapeId = FindShape( elem );
2991 if ( aShapeId != helper.GetSubShapeID() ) {
2995 shape = aMesh->IndexToShape( aShapeId );
2996 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2997 TopoDS_Face face = TopoDS::Face( shape );
2998 surface = BRep_Tool::Surface( face );
2999 if ( !surface.IsNull() )
3000 helper.SetSubShape( shape );
3004 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3005 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3006 for ( int i = 0; itN->more(); ++i )
3007 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3009 const SMDS_MeshNode* centrNode = aNodes[8];
3010 if ( centrNode == 0 )
3012 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3013 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3015 myLastCreatedNodes.push_back(centrNode);
3018 // create a new element
3019 const SMDS_MeshElement* newElem1 = 0;
3020 const SMDS_MeshElement* newElem2 = 0;
3022 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3023 aNodes[6], aNodes[7], centrNode );
3024 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3025 centrNode, aNodes[4], aNodes[5] );
3028 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3029 aNodes[7], aNodes[4], centrNode );
3030 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3031 centrNode, aNodes[5], aNodes[6] );
3033 myLastCreatedElems.push_back(newElem1);
3034 myLastCreatedElems.push_back(newElem2);
3035 // put a new triangle on the same shape and add to the same groups
3038 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3039 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3041 AddToSameGroups( newElem1, elem, aMesh );
3042 AddToSameGroups( newElem2, elem, aMesh );
3043 aMesh->RemoveElement( elem );
3050 //=======================================================================
3051 //function : getAngle
3053 //=======================================================================
3055 double getAngle(const SMDS_MeshElement * tr1,
3056 const SMDS_MeshElement * tr2,
3057 const SMDS_MeshNode * n1,
3058 const SMDS_MeshNode * n2)
3060 double angle = 2. * M_PI; // bad angle
3063 SMESH::Controls::TSequenceOfXYZ P1, P2;
3064 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3065 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3068 if(!tr1->IsQuadratic())
3069 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3071 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3072 if ( N1.SquareMagnitude() <= gp::Resolution() )
3074 if(!tr2->IsQuadratic())
3075 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3077 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3078 if ( N2.SquareMagnitude() <= gp::Resolution() )
3081 // find the first diagonal node n1 in the triangles:
3082 // take in account a diagonal link orientation
3083 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3084 for ( int t = 0; t < 2; t++ ) {
3085 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3086 int i = 0, iDiag = -1;
3087 while ( it->more()) {
3088 const SMDS_MeshElement *n = it->next();
3089 if ( n == n1 || n == n2 ) {
3093 if ( i - iDiag == 1 )
3094 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3103 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3106 angle = N1.Angle( N2 );
3111 // =================================================
3112 // class generating a unique ID for a pair of nodes
3113 // and able to return nodes by that ID
3114 // =================================================
3118 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3119 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3122 long GetLinkID (const SMDS_MeshNode * n1,
3123 const SMDS_MeshNode * n2) const
3125 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3128 bool GetNodes (const long theLinkID,
3129 const SMDS_MeshNode* & theNode1,
3130 const SMDS_MeshNode* & theNode2) const
3132 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3133 if ( !theNode1 ) return false;
3134 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3135 if ( !theNode2 ) return false;
3141 const SMESHDS_Mesh* myMesh;
3146 //=======================================================================
3147 //function : TriToQuad
3148 //purpose : Fuse neighbour triangles into quadrangles.
3149 // theCrit is used to select a neighbour to fuse with.
3150 // theMaxAngle is a max angle between element normals at which
3151 // fusion is still performed.
3152 //=======================================================================
3154 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3155 SMESH::Controls::NumericalFunctorPtr theCrit,
3156 const double theMaxAngle)
3159 myLastCreatedElems.reserve( theElems.size() / 2 );
3161 if ( !theCrit.get() )
3164 SMESHDS_Mesh * aMesh = GetMeshDS();
3166 // Prepare data for algo: build
3167 // 1. map of elements with their linkIDs
3168 // 2. map of linkIDs with their elements
3170 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3171 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3172 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3173 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3175 TIDSortedElemSet::iterator itElem;
3176 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3178 const SMDS_MeshElement* elem = *itElem;
3179 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3180 bool IsTria = ( elem->NbCornerNodes()==3 );
3181 if (!IsTria) continue;
3183 // retrieve element nodes
3184 const SMDS_MeshNode* aNodes [4];
3185 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3188 aNodes[ i++ ] = itN->next();
3189 aNodes[ 3 ] = aNodes[ 0 ];
3192 for ( i = 0; i < 3; i++ ) {
3193 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3194 // check if elements sharing a link can be fused
3195 itLE = mapLi_listEl.find( link );
3196 if ( itLE != mapLi_listEl.end() ) {
3197 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3199 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3200 //if ( FindShape( elem ) != FindShape( elem2 ))
3201 // continue; // do not fuse triangles laying on different shapes
3202 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3203 continue; // avoid making badly shaped quads
3204 (*itLE).second.push_back( elem );
3207 mapLi_listEl[ link ].push_back( elem );
3209 mapEl_setLi [ elem ].insert( link );
3212 // Clean the maps from the links shared by a sole element, ie
3213 // links to which only one element is bound in mapLi_listEl
3215 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3216 int nbElems = (*itLE).second.size();
3217 if ( nbElems < 2 ) {
3218 const SMDS_MeshElement* elem = (*itLE).second.front();
3219 SMESH_TLink link = (*itLE).first;
3220 mapEl_setLi[ elem ].erase( link );
3221 if ( mapEl_setLi[ elem ].empty() )
3222 mapEl_setLi.erase( elem );
3226 // Algo: fuse triangles into quadrangles
3228 while ( ! mapEl_setLi.empty() ) {
3229 // Look for the start element:
3230 // the element having the least nb of shared links
3231 const SMDS_MeshElement* startElem = 0;
3233 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3234 int nbLinks = (*itEL).second.size();
3235 if ( nbLinks < minNbLinks ) {
3236 startElem = (*itEL).first;
3237 minNbLinks = nbLinks;
3238 if ( minNbLinks == 1 )
3243 // search elements to fuse starting from startElem or links of elements
3244 // fused earlyer - startLinks
3245 list< SMESH_TLink > startLinks;
3246 while ( startElem || !startLinks.empty() ) {
3247 while ( !startElem && !startLinks.empty() ) {
3248 // Get an element to start, by a link
3249 SMESH_TLink linkId = startLinks.front();
3250 startLinks.pop_front();
3251 itLE = mapLi_listEl.find( linkId );
3252 if ( itLE != mapLi_listEl.end() ) {
3253 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3254 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3255 for ( ; itE != listElem.end() ; itE++ )
3256 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3258 mapLi_listEl.erase( itLE );
3263 // Get candidates to be fused
3264 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3265 const SMESH_TLink *link12 = 0, *link13 = 0;
3267 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3268 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3269 ASSERT( !setLi.empty() );
3270 set< SMESH_TLink >::iterator itLi;
3271 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3273 const SMESH_TLink & link = (*itLi);
3274 itLE = mapLi_listEl.find( link );
3275 if ( itLE == mapLi_listEl.end() )
3278 const SMDS_MeshElement* elem = (*itLE).second.front();
3280 elem = (*itLE).second.back();
3281 mapLi_listEl.erase( itLE );
3282 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3293 // add other links of elem to list of links to re-start from
3294 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3295 set< SMESH_TLink >::iterator it;
3296 for ( it = links.begin(); it != links.end(); it++ ) {
3297 const SMESH_TLink& link2 = (*it);
3298 if ( link2 != link )
3299 startLinks.push_back( link2 );
3303 // Get nodes of possible quadrangles
3304 const SMDS_MeshNode *n12 [4], *n13 [4];
3305 bool Ok12 = false, Ok13 = false;
3306 const SMDS_MeshNode *linkNode1, *linkNode2;
3308 linkNode1 = link12->first;
3309 linkNode2 = link12->second;
3310 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3314 linkNode1 = link13->first;
3315 linkNode2 = link13->second;
3316 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3320 // Choose a pair to fuse
3321 if ( Ok12 && Ok13 ) {
3322 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3323 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3324 double aBadRate12 = getBadRate( &quad12, theCrit );
3325 double aBadRate13 = getBadRate( &quad13, theCrit );
3326 if ( aBadRate13 < aBadRate12 )
3333 // and remove fused elems and remove links from the maps
3334 mapEl_setLi.erase( tr1 );
3337 mapEl_setLi.erase( tr2 );
3338 mapLi_listEl.erase( *link12 );
3339 if ( tr1->NbNodes() == 3 )
3341 const SMDS_MeshElement* newElem = 0;
3342 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3343 myLastCreatedElems.push_back(newElem);
3344 AddToSameGroups( newElem, tr1, aMesh );
3345 int aShapeId = tr1->getshapeId();
3347 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3348 aMesh->RemoveElement( tr1 );
3349 aMesh->RemoveElement( tr2 );
3352 vector< const SMDS_MeshNode* > N1;
3353 vector< const SMDS_MeshNode* > N2;
3354 getNodesFromTwoTria(tr1,tr2,N1,N2);
3355 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3356 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3357 // i.e. first nodes from both arrays form a new diagonal
3358 const SMDS_MeshNode* aNodes[8];
3367 const SMDS_MeshElement* newElem = 0;
3368 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3369 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3372 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3373 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3374 myLastCreatedElems.push_back(newElem);
3375 AddToSameGroups( newElem, tr1, aMesh );
3376 int aShapeId = tr1->getshapeId();
3378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379 aMesh->RemoveElement( tr1 );
3380 aMesh->RemoveElement( tr2 );
3381 // remove middle node (9)
3382 if ( N1[4]->NbInverseElements() == 0 )
3383 aMesh->RemoveNode( N1[4] );
3384 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3385 aMesh->RemoveNode( N1[6] );
3386 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3387 aMesh->RemoveNode( N2[6] );
3392 mapEl_setLi.erase( tr3 );
3393 mapLi_listEl.erase( *link13 );
3394 if ( tr1->NbNodes() == 3 ) {
3395 const SMDS_MeshElement* newElem = 0;
3396 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3397 myLastCreatedElems.push_back(newElem);
3398 AddToSameGroups( newElem, tr1, aMesh );
3399 int aShapeId = tr1->getshapeId();
3401 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3402 aMesh->RemoveElement( tr1 );
3403 aMesh->RemoveElement( tr3 );
3406 vector< const SMDS_MeshNode* > N1;
3407 vector< const SMDS_MeshNode* > N2;
3408 getNodesFromTwoTria(tr1,tr3,N1,N2);
3409 // now we receive following N1 and N2 (using numeration as above image)
3410 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3411 // i.e. first nodes from both arrays form a new diagonal
3412 const SMDS_MeshNode* aNodes[8];
3421 const SMDS_MeshElement* newElem = 0;
3422 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3423 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3426 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3427 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3428 myLastCreatedElems.push_back(newElem);
3429 AddToSameGroups( newElem, tr1, aMesh );
3430 int aShapeId = tr1->getshapeId();
3432 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3433 aMesh->RemoveElement( tr1 );
3434 aMesh->RemoveElement( tr3 );
3435 // remove middle node (9)
3436 if ( N1[4]->NbInverseElements() == 0 )
3437 aMesh->RemoveNode( N1[4] );
3438 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3439 aMesh->RemoveNode( N1[6] );
3440 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3441 aMesh->RemoveNode( N2[6] );
3445 // Next element to fuse: the rejected one
3447 startElem = Ok12 ? tr3 : tr2;
3449 } // if ( startElem )
3450 } // while ( startElem || !startLinks.empty() )
3451 } // while ( ! mapEl_setLi.empty() )
3456 //================================================================================
3458 * \brief Return nodes linked to the given one
3459 * \param theNode - the node
3460 * \param linkedNodes - the found nodes
3461 * \param type - the type of elements to check
3463 * Medium nodes are ignored
3465 //================================================================================
3467 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3468 TIDSortedElemSet & linkedNodes,
3469 SMDSAbs_ElementType type )
3471 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3472 while ( elemIt->more() )
3474 const SMDS_MeshElement* elem = elemIt->next();
3475 if(elem->GetType() == SMDSAbs_0DElement)
3478 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3479 if ( elem->GetType() == SMDSAbs_Volume )
3481 SMDS_VolumeTool vol( elem );
3482 while ( nodeIt->more() ) {
3483 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3484 if ( theNode != n && vol.IsLinked( theNode, n ))
3485 linkedNodes.insert( n );
3490 for ( int i = 0; nodeIt->more(); ++i ) {
3491 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3492 if ( n == theNode ) {
3493 int iBefore = i - 1;
3495 if ( elem->IsQuadratic() ) {
3496 int nb = elem->NbNodes() / 2;
3497 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3498 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3500 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3501 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3508 //=======================================================================
3509 //function : laplacianSmooth
3510 //purpose : pulls theNode toward the center of surrounding nodes directly
3511 // connected to that node along an element edge
3512 //=======================================================================
3514 void laplacianSmooth(const SMDS_MeshNode* theNode,
3515 const Handle(Geom_Surface)& theSurface,
3516 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3518 // find surrounding nodes
3520 TIDSortedElemSet nodeSet;
3521 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3523 // compute new coodrs
3525 double coord[] = { 0., 0., 0. };
3526 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3527 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3528 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3529 if ( theSurface.IsNull() ) { // smooth in 3D
3530 coord[0] += node->X();
3531 coord[1] += node->Y();
3532 coord[2] += node->Z();
3534 else { // smooth in 2D
3535 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3536 gp_XY* uv = theUVMap[ node ];
3537 coord[0] += uv->X();
3538 coord[1] += uv->Y();
3541 int nbNodes = nodeSet.size();
3544 coord[0] /= nbNodes;
3545 coord[1] /= nbNodes;
3547 if ( !theSurface.IsNull() ) {
3548 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3549 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3550 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3556 coord[2] /= nbNodes;
3560 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3563 //=======================================================================
3564 //function : centroidalSmooth
3565 //purpose : pulls theNode toward the element-area-weighted centroid of the
3566 // surrounding elements
3567 //=======================================================================
3569 void centroidalSmooth(const SMDS_MeshNode* theNode,
3570 const Handle(Geom_Surface)& theSurface,
3571 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3573 gp_XYZ aNewXYZ(0.,0.,0.);
3574 SMESH::Controls::Area anAreaFunc;
3575 double totalArea = 0.;
3580 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3581 while ( elemIt->more() )
3583 const SMDS_MeshElement* elem = elemIt->next();
3586 gp_XYZ elemCenter(0.,0.,0.);
3587 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3588 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3589 int nn = elem->NbNodes();
3590 if(elem->IsQuadratic()) nn = nn/2;
3592 //while ( itN->more() ) {
3594 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3596 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3597 aNodePoints.push_back( aP );
3598 if ( !theSurface.IsNull() ) { // smooth in 2D
3599 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3600 gp_XY* uv = theUVMap[ aNode ];
3601 aP.SetCoord( uv->X(), uv->Y(), 0. );
3605 double elemArea = anAreaFunc.GetValue( aNodePoints );
3606 totalArea += elemArea;
3608 aNewXYZ += elemCenter * elemArea;
3610 aNewXYZ /= totalArea;
3611 if ( !theSurface.IsNull() ) {
3612 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3613 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3618 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3621 //=======================================================================
3622 //function : getClosestUV
3623 //purpose : return UV of closest projection
3624 //=======================================================================
3626 static bool getClosestUV (Extrema_GenExtPS& projector,
3627 const gp_Pnt& point,
3630 projector.Perform( point );
3631 if ( projector.IsDone() ) {
3632 double u, v, minVal = DBL_MAX;
3633 for ( int i = projector.NbExt(); i > 0; i-- )
3634 if ( projector.SquareDistance( i ) < minVal ) {
3635 minVal = projector.SquareDistance( i );
3636 projector.Point( i ).Parameter( u, v );
3638 result.SetCoord( u, v );
3644 //=======================================================================
3646 //purpose : Smooth theElements during theNbIterations or until a worst
3647 // element has aspect ratio <= theTgtAspectRatio.
3648 // Aspect Ratio varies in range [1.0, inf].
3649 // If theElements is empty, the whole mesh is smoothed.
3650 // theFixedNodes contains additionally fixed nodes. Nodes built
3651 // on edges and boundary nodes are always fixed.
3652 //=======================================================================
3654 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3655 set<const SMDS_MeshNode*> & theFixedNodes,
3656 const SmoothMethod theSmoothMethod,
3657 const int theNbIterations,
3658 double theTgtAspectRatio,
3663 if ( theTgtAspectRatio < 1.0 )
3664 theTgtAspectRatio = 1.0;
3666 const double disttol = 1.e-16;
3668 SMESH::Controls::AspectRatio aQualityFunc;
3670 SMESHDS_Mesh* aMesh = GetMeshDS();
3672 if ( theElems.empty() ) {
3673 // add all faces to theElems
3674 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3675 while ( fIt->more() ) {
3676 const SMDS_MeshElement* face = fIt->next();
3677 theElems.insert( theElems.end(), face );
3680 // get all face ids theElems are on
3681 set< int > faceIdSet;
3682 TIDSortedElemSet::iterator itElem;
3684 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3685 int fId = FindShape( *itElem );
3686 // check that corresponding submesh exists and a shape is face
3688 faceIdSet.find( fId ) == faceIdSet.end() &&
3689 aMesh->MeshElements( fId )) {
3690 TopoDS_Shape F = aMesh->IndexToShape( fId );
3691 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3692 faceIdSet.insert( fId );
3695 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3697 // ===============================================
3698 // smooth elements on each TopoDS_Face separately
3699 // ===============================================
3701 SMESH_MesherHelper helper( *GetMesh() );
3703 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3704 for ( ; fId != faceIdSet.rend(); ++fId )
3706 // get face surface and submesh
3707 Handle(Geom_Surface) surface;
3708 SMESHDS_SubMesh* faceSubMesh = 0;
3711 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3712 bool isUPeriodic = false, isVPeriodic = false;
3715 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3716 surface = BRep_Tool::Surface( face );
3717 faceSubMesh = aMesh->MeshElements( *fId );
3718 fToler2 = BRep_Tool::Tolerance( face );
3719 fToler2 *= fToler2 * 10.;
3720 isUPeriodic = surface->IsUPeriodic();
3721 // if ( isUPeriodic )
3722 // surface->UPeriod();
3723 isVPeriodic = surface->IsVPeriodic();
3724 // if ( isVPeriodic )
3725 // surface->VPeriod();
3726 surface->Bounds( u1, u2, v1, v2 );
3727 helper.SetSubShape( face );
3729 // ---------------------------------------------------------
3730 // for elements on a face, find movable and fixed nodes and
3731 // compute UV for them
3732 // ---------------------------------------------------------
3733 bool checkBoundaryNodes = false;
3734 bool isQuadratic = false;
3735 set<const SMDS_MeshNode*> setMovableNodes;
3736 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3737 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3738 list< const SMDS_MeshElement* > elemsOnFace;
3740 Extrema_GenExtPS projector;
3741 GeomAdaptor_Surface surfAdaptor;
3742 if ( !surface.IsNull() ) {
3743 surfAdaptor.Load( surface );
3744 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3746 int nbElemOnFace = 0;
3747 itElem = theElems.begin();
3748 // loop on not yet smoothed elements: look for elems on a face
3749 while ( itElem != theElems.end() )
3751 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3752 break; // all elements found
3754 const SMDS_MeshElement* elem = *itElem;
3755 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3756 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3760 elemsOnFace.push_back( elem );
3761 theElems.erase( itElem++ );
3765 isQuadratic = elem->IsQuadratic();
3767 // get movable nodes of elem
3768 const SMDS_MeshNode* node;
3769 SMDS_TypeOfPosition posType;
3770 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3771 int nn = 0, nbn = elem->NbNodes();
3772 if(elem->IsQuadratic())
3774 while ( nn++ < nbn ) {
3775 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3776 const SMDS_PositionPtr& pos = node->GetPosition();
3777 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3778 if (posType != SMDS_TOP_EDGE &&
3779 posType != SMDS_TOP_VERTEX &&
3780 theFixedNodes.find( node ) == theFixedNodes.end())
3782 // check if all faces around the node are on faceSubMesh
3783 // because a node on edge may be bound to face
3785 if ( faceSubMesh ) {
3786 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3787 while ( eIt->more() && all ) {
3788 const SMDS_MeshElement* e = eIt->next();
3789 all = faceSubMesh->Contains( e );
3793 setMovableNodes.insert( node );
3795 checkBoundaryNodes = true;
3797 if ( posType == SMDS_TOP_3DSPACE )
3798 checkBoundaryNodes = true;
3801 if ( surface.IsNull() )
3804 // get nodes to check UV
3805 list< const SMDS_MeshNode* > uvCheckNodes;
3806 const SMDS_MeshNode* nodeInFace = 0;
3807 itN = elem->nodesIterator();
3808 nn = 0; nbn = elem->NbNodes();
3809 if(elem->IsQuadratic())
3811 while ( nn++ < nbn ) {
3812 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3813 if ( node->GetPosition()->GetDim() == 2 )
3815 if ( uvMap.find( node ) == uvMap.end() )
3816 uvCheckNodes.push_back( node );
3817 // add nodes of elems sharing node
3818 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3819 // while ( eIt->more() ) {
3820 // const SMDS_MeshElement* e = eIt->next();
3821 // if ( e != elem ) {
3822 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3823 // while ( nIt->more() ) {
3824 // const SMDS_MeshNode* n =
3825 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3826 // if ( uvMap.find( n ) == uvMap.end() )
3827 // uvCheckNodes.push_back( n );
3833 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3834 for ( ; n != uvCheckNodes.end(); ++n ) {
3837 const SMDS_PositionPtr& pos = node->GetPosition();
3838 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842 bool toCheck = true;
3843 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3845 // compute not existing UV
3846 bool project = ( posType == SMDS_TOP_3DSPACE );
3847 // double dist1 = DBL_MAX, dist2 = 0;
3848 // if ( posType != SMDS_TOP_3DSPACE ) {
3849 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3850 // project = dist1 > fToler2;
3852 if ( project ) { // compute new UV
3854 gp_Pnt pNode = SMESH_NodeXYZ( node );
3855 if ( !getClosestUV( projector, pNode, newUV )) {
3856 MESSAGE("Node Projection Failed " << node);
3860 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3862 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3864 // if ( posType != SMDS_TOP_3DSPACE )
3865 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3866 // if ( dist2 < dist1 )
3870 // store UV in the map
3871 listUV.push_back( uv );
3872 uvMap.insert( make_pair( node, &listUV.back() ));
3874 } // loop on not yet smoothed elements
3876 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3877 checkBoundaryNodes = true;
3879 // fix nodes on mesh boundary
3881 if ( checkBoundaryNodes ) {
3882 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3883 map< SMESH_TLink, int >::iterator link_nb;
3884 // put all elements links to linkNbMap
3885 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3886 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3887 const SMDS_MeshElement* elem = (*elemIt);
3888 int nbn = elem->NbCornerNodes();
3889 // loop on elem links: insert them in linkNbMap
3890 for ( int iN = 0; iN < nbn; ++iN ) {
3891 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3892 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3893 SMESH_TLink link( n1, n2 );
3894 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3898 // remove nodes that are in links encountered only once from setMovableNodes
3899 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3900 if ( link_nb->second == 1 ) {
3901 setMovableNodes.erase( link_nb->first.node1() );
3902 setMovableNodes.erase( link_nb->first.node2() );
3907 // -----------------------------------------------------
3908 // for nodes on seam edge, compute one more UV ( uvMap2 );
3909 // find movable nodes linked to nodes on seam and which
3910 // are to be smoothed using the second UV ( uvMap2 )
3911 // -----------------------------------------------------
3913 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3914 if ( !surface.IsNull() ) {
3915 TopExp_Explorer eExp( face, TopAbs_EDGE );
3916 for ( ; eExp.More(); eExp.Next() ) {
3917 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3918 if ( !BRep_Tool::IsClosed( edge, face ))
3920 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3921 if ( !sm ) continue;
3922 // find out which parameter varies for a node on seam
3925 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3926 if ( pcurve.IsNull() ) continue;
3927 uv1 = pcurve->Value( f );
3929 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3930 if ( pcurve.IsNull() ) continue;
3931 uv2 = pcurve->Value( f );
3932 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3934 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3935 std::swap( uv1, uv2 );
3936 // get nodes on seam and its vertices
3937 list< const SMDS_MeshNode* > seamNodes;
3938 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3939 while ( nSeamIt->more() ) {
3940 const SMDS_MeshNode* node = nSeamIt->next();
3941 if ( !isQuadratic || !IsMedium( node ))
3942 seamNodes.push_back( node );
3944 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3945 for ( ; vExp.More(); vExp.Next() ) {
3946 sm = aMesh->MeshElements( vExp.Current() );
3948 nSeamIt = sm->GetNodes();
3949 while ( nSeamIt->more() )
3950 seamNodes.push_back( nSeamIt->next() );
3953 // loop on nodes on seam
3954 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3955 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3956 const SMDS_MeshNode* nSeam = *noSeIt;
3957 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3958 if ( n_uv == uvMap.end() )
3961 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3962 // set the second UV
3963 listUV.push_back( *n_uv->second );
3964 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3965 if ( uvMap2.empty() )
3966 uvMap2 = uvMap; // copy the uvMap contents
3967 uvMap2[ nSeam ] = &listUV.back();
3969 // collect movable nodes linked to ones on seam in nodesNearSeam
3970 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3971 while ( eIt->more() ) {
3972 const SMDS_MeshElement* e = eIt->next();
3973 int nbUseMap1 = 0, nbUseMap2 = 0;
3974 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3975 int nn = 0, nbn = e->NbNodes();
3976 if(e->IsQuadratic()) nbn = nbn/2;
3977 while ( nn++ < nbn )
3979 const SMDS_MeshNode* n =
3980 static_cast<const SMDS_MeshNode*>( nIt->next() );
3982 setMovableNodes.find( n ) == setMovableNodes.end() )
3984 // add only nodes being closer to uv2 than to uv1
3985 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3986 // 0.5 * ( n->Y() + nSeam->Y() ),
3987 // 0.5 * ( n->Z() + nSeam->Z() ));
3989 // getClosestUV( projector, pMid, uv );
3990 double x = uvMap[ n ]->Coord( iPar );
3991 if ( Abs( uv1.Coord( iPar ) - x ) >
3992 Abs( uv2.Coord( iPar ) - x )) {
3993 nodesNearSeam.insert( n );
3999 // for centroidalSmooth all element nodes must
4000 // be on one side of a seam
4001 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4002 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4004 while ( nn++ < nbn ) {
4005 const SMDS_MeshNode* n =
4006 static_cast<const SMDS_MeshNode*>( nIt->next() );
4007 setMovableNodes.erase( n );
4011 } // loop on nodes on seam
4012 } // loop on edge of a face
4013 } // if ( !face.IsNull() )
4015 if ( setMovableNodes.empty() ) {
4016 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4017 continue; // goto next face
4025 double maxRatio = -1., maxDisplacement = -1.;
4026 set<const SMDS_MeshNode*>::iterator nodeToMove;
4027 for ( it = 0; it < theNbIterations; it++ ) {
4028 maxDisplacement = 0.;
4029 nodeToMove = setMovableNodes.begin();
4030 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4031 const SMDS_MeshNode* node = (*nodeToMove);
4032 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4035 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4036 if ( theSmoothMethod == LAPLACIAN )
4037 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4039 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4041 // node displacement
4042 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4043 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4044 if ( aDispl > maxDisplacement )
4045 maxDisplacement = aDispl;
4047 // no node movement => exit
4048 //if ( maxDisplacement < 1.e-16 ) {
4049 if ( maxDisplacement < disttol ) {
4050 MESSAGE("-- no node movement --");
4054 // check elements quality
4056 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4057 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4058 const SMDS_MeshElement* elem = (*elemIt);
4059 if ( !elem || elem->GetType() != SMDSAbs_Face )
4061 SMESH::Controls::TSequenceOfXYZ aPoints;
4062 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4063 double aValue = aQualityFunc.GetValue( aPoints );
4064 if ( aValue > maxRatio )
4068 if ( maxRatio <= theTgtAspectRatio ) {
4069 //MESSAGE("-- quality achieved --");
4072 if (it+1 == theNbIterations) {
4073 //MESSAGE("-- Iteration limit exceeded --");
4075 } // smoothing iterations
4077 // MESSAGE(" Face id: " << *fId <<
4078 // " Nb iterstions: " << it <<
4079 // " Displacement: " << maxDisplacement <<
4080 // " Aspect Ratio " << maxRatio);
4082 // ---------------------------------------
4083 // new nodes positions are computed,
4084 // record movement in DS and set new UV
4085 // ---------------------------------------
4086 nodeToMove = setMovableNodes.begin();
4087 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4088 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4089 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4090 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4091 if ( node_uv != uvMap.end() ) {
4092 gp_XY* uv = node_uv->second;
4094 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4098 // move medium nodes of quadratic elements
4101 vector<const SMDS_MeshNode*> nodes;
4103 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4104 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4106 const SMDS_MeshElement* QF = *elemIt;
4107 if ( QF->IsQuadratic() )
4109 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4110 SMDS_MeshElement::iterator() );
4111 nodes.push_back( nodes[0] );
4113 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4115 if ( !surface.IsNull() )
4117 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4118 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4119 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4120 xyz = surface->Value( uv.X(), uv.Y() );
4123 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4125 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4126 // we have to move a medium node
4127 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4133 } // loop on face ids
4139 //=======================================================================
4140 //function : isReverse
4141 //purpose : Return true if normal of prevNodes is not co-directied with
4142 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4143 // iNotSame is where prevNodes and nextNodes are different.
4144 // If result is true then future volume orientation is OK
4145 //=======================================================================
4147 bool isReverse(const SMDS_MeshElement* face,
4148 const vector<const SMDS_MeshNode*>& prevNodes,
4149 const vector<const SMDS_MeshNode*>& nextNodes,
4153 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4154 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4155 gp_XYZ extrDir( pN - pP ), faceNorm;
4156 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4158 return faceNorm * extrDir < 0.0;
4161 //================================================================================
4163 * \brief Assure that theElemSets[0] holds elements, not nodes
4165 //================================================================================
4167 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4169 if ( !theElemSets[0].empty() &&
4170 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4172 std::swap( theElemSets[0], theElemSets[1] );
4174 else if ( !theElemSets[1].empty() &&
4175 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4177 std::swap( theElemSets[0], theElemSets[1] );
4182 //=======================================================================
4184 * \brief Create elements by sweeping an element
4185 * \param elem - element to sweep
4186 * \param newNodesItVec - nodes generated from each node of the element
4187 * \param newElems - generated elements
4188 * \param nbSteps - number of sweeping steps
4189 * \param srcElements - to append elem for each generated element
4191 //=======================================================================
4193 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4194 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4195 list<const SMDS_MeshElement*>& newElems,
4196 const size_t nbSteps,
4197 SMESH_SequenceOfElemPtr& srcElements)
4199 SMESHDS_Mesh* aMesh = GetMeshDS();
4201 const int nbNodes = elem->NbNodes();
4202 const int nbCorners = elem->NbCornerNodes();
4203 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4204 polyhedron creation !!! */
4205 // Loop on elem nodes:
4206 // find new nodes and detect same nodes indices
4207 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4208 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4209 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4210 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4212 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4213 vector<int> sames(nbNodes);
4214 vector<bool> isSingleNode(nbNodes);
4216 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4217 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4218 const SMDS_MeshNode* node = nnIt->first;
4219 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4220 if ( listNewNodes.empty() )
4223 itNN [ iNode ] = listNewNodes.begin();
4224 prevNod[ iNode ] = node;
4225 nextNod[ iNode ] = listNewNodes.front();
4227 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4228 corner node of linear */
4229 if ( prevNod[ iNode ] != nextNod [ iNode ])
4230 nbDouble += !isSingleNode[iNode];
4232 if( iNode < nbCorners ) { // check corners only
4233 if ( prevNod[ iNode ] == nextNod [ iNode ])
4234 sames[nbSame++] = iNode;
4236 iNotSameNode = iNode;
4240 if ( nbSame == nbNodes || nbSame > 2) {
4241 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4245 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4247 // fix nodes order to have bottom normal external
4248 if ( baseType == SMDSEntity_Polygon )
4250 std::reverse( itNN.begin(), itNN.end() );
4251 std::reverse( prevNod.begin(), prevNod.end() );
4252 std::reverse( midlNod.begin(), midlNod.end() );
4253 std::reverse( nextNod.begin(), nextNod.end() );
4254 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4258 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4259 SMDS_MeshCell::applyInterlace( ind, itNN );
4260 SMDS_MeshCell::applyInterlace( ind, prevNod );
4261 SMDS_MeshCell::applyInterlace( ind, nextNod );
4262 SMDS_MeshCell::applyInterlace( ind, midlNod );
4263 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4266 sames[nbSame] = iNotSameNode;
4267 for ( int j = 0; j <= nbSame; ++j )
4268 for ( size_t i = 0; i < ind.size(); ++i )
4269 if ( ind[i] == sames[j] )
4274 iNotSameNode = sames[nbSame];
4278 else if ( elem->GetType() == SMDSAbs_Edge )
4280 // orient a new face same as adjacent one
4282 const SMDS_MeshElement* e;
4283 TIDSortedElemSet dummy;
4284 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4285 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4286 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4288 // there is an adjacent face, check order of nodes in it
4289 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4292 std::swap( itNN[0], itNN[1] );
4293 std::swap( prevNod[0], prevNod[1] );
4294 std::swap( nextNod[0], nextNod[1] );
4295 std::swap( isSingleNode[0], isSingleNode[1] );
4297 sames[0] = 1 - sames[0];
4298 iNotSameNode = 1 - iNotSameNode;
4303 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4305 iSameNode = sames[ nbSame-1 ];
4306 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4307 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4308 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4311 if ( baseType == SMDSEntity_Polygon )
4313 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4314 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4316 else if ( baseType == SMDSEntity_Quad_Polygon )
4318 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4319 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4322 // make new elements
4323 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4326 for ( iNode = 0; iNode < nbNodes; iNode++ )
4328 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4329 nextNod[ iNode ] = *itNN[ iNode ]++;
4332 SMDS_MeshElement* aNewElem = 0;
4333 /*if(!elem->IsPoly())*/ {
4334 switch ( baseType ) {
4336 case SMDSEntity_Node: { // sweep NODE
4337 if ( nbSame == 0 ) {
4338 if ( isSingleNode[0] )
4339 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4341 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4347 case SMDSEntity_Edge: { // sweep EDGE
4348 if ( nbDouble == 0 )
4350 if ( nbSame == 0 ) // ---> quadrangle
4351 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4352 nextNod[ 1 ], nextNod[ 0 ] );
4353 else // ---> triangle
4354 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4355 nextNod[ iNotSameNode ] );
4357 else // ---> polygon
4359 vector<const SMDS_MeshNode*> poly_nodes;
4360 poly_nodes.push_back( prevNod[0] );
4361 poly_nodes.push_back( prevNod[1] );
4362 if ( prevNod[1] != nextNod[1] )
4364 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4365 poly_nodes.push_back( nextNod[1] );
4367 if ( prevNod[0] != nextNod[0] )
4369 poly_nodes.push_back( nextNod[0] );
4370 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4372 switch ( poly_nodes.size() ) {
4374 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4377 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4378 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4381 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4386 case SMDSEntity_Triangle: // TRIANGLE --->
4388 if ( nbDouble > 0 ) break;
4389 if ( nbSame == 0 ) // ---> pentahedron
4390 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4391 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4393 else if ( nbSame == 1 ) // ---> pyramid
4394 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4395 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4396 nextNod[ iSameNode ]);
4398 else // 2 same nodes: ---> tetrahedron
4399 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4400 nextNod[ iNotSameNode ]);
4403 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4407 if ( nbDouble+nbSame == 2 )
4409 if(nbSame==0) { // ---> quadratic quadrangle
4410 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4411 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4413 else { //(nbSame==1) // ---> quadratic triangle
4415 return; // medium node on axis
4417 else if(sames[0]==0)
4418 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4419 prevNod[2], midlNod[1], nextNod[2] );
4421 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4422 prevNod[2], nextNod[2], midlNod[0]);
4425 else if ( nbDouble == 3 )
4427 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4428 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4429 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4436 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4437 if ( nbDouble > 0 ) break;
4439 if ( nbSame == 0 ) // ---> hexahedron
4440 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4441 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4443 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4444 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4445 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4446 nextNod[ iSameNode ]);
4447 newElems.push_back( aNewElem );
4448 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4449 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4450 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4452 else if ( nbSame == 2 ) { // ---> pentahedron
4453 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4454 // iBeforeSame is same too
4455 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4456 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4457 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4459 // iAfterSame is same too
4460 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4461 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4462 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4466 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4467 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4468 if ( nbDouble+nbSame != 3 ) break;
4470 // ---> pentahedron with 15 nodes
4471 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4472 nextNod[0], nextNod[1], nextNod[2],
4473 prevNod[3], prevNod[4], prevNod[5],
4474 nextNod[3], nextNod[4], nextNod[5],
4475 midlNod[0], midlNod[1], midlNod[2]);
4477 else if(nbSame==1) {
4478 // ---> 2d order pyramid of 13 nodes
4479 int apex = iSameNode;
4480 int i0 = ( apex + 1 ) % nbCorners;
4481 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4485 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4486 nextNod[i0], nextNod[i1], prevNod[apex],
4487 prevNod[i01], midlNod[i0],
4488 nextNod[i01], midlNod[i1],
4489 prevNod[i1a], prevNod[i0a],
4490 nextNod[i0a], nextNod[i1a]);
4492 else if(nbSame==2) {
4493 // ---> 2d order tetrahedron of 10 nodes
4494 int n1 = iNotSameNode;
4495 int n2 = ( n1 + 1 ) % nbCorners;
4496 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4500 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4501 prevNod[n12], prevNod[n23], prevNod[n31],
4502 midlNod[n1], nextNod[n12], nextNod[n31]);
4506 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4508 if ( nbDouble != 4 ) break;
4509 // ---> hexahedron with 20 nodes
4510 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4511 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4512 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4513 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4514 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4516 else if(nbSame==1) {
4517 // ---> pyramid + pentahedron - can not be created since it is needed
4518 // additional middle node at the center of face
4519 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4522 else if( nbSame == 2 ) {
4523 if ( nbDouble != 2 ) break;
4524 // ---> 2d order Pentahedron with 15 nodes
4526 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4527 // iBeforeSame is same too
4534 // iAfterSame is same too
4544 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4545 prevNod[n4], prevNod[n5], nextNod[n5],
4546 prevNod[n12], midlNod[n2], nextNod[n12],
4547 prevNod[n45], midlNod[n5], nextNod[n45],
4548 prevNod[n14], prevNod[n25], nextNod[n25]);
4552 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4554 if( nbSame == 0 && nbDouble == 9 ) {
4555 // ---> tri-quadratic hexahedron with 27 nodes
4556 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4557 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4558 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4559 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4560 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4561 prevNod[8], // bottom center
4562 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4563 nextNod[8], // top center
4564 midlNod[8]);// elem center
4572 case SMDSEntity_Polygon: { // sweep POLYGON
4574 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4575 // ---> hexagonal prism
4576 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4577 prevNod[3], prevNod[4], prevNod[5],
4578 nextNod[0], nextNod[1], nextNod[2],
4579 nextNod[3], nextNod[4], nextNod[5]);
4583 case SMDSEntity_Ball:
4588 } // switch ( baseType )
4591 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4593 if ( baseType != SMDSEntity_Polygon )
4595 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4596 SMDS_MeshCell::applyInterlace( ind, prevNod );
4597 SMDS_MeshCell::applyInterlace( ind, nextNod );
4598 SMDS_MeshCell::applyInterlace( ind, midlNod );
4599 SMDS_MeshCell::applyInterlace( ind, itNN );
4600 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4601 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4603 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4604 vector<int> quantities (nbNodes + 2);
4605 polyedre_nodes.clear();
4609 for (int inode = 0; inode < nbNodes; inode++)
4610 polyedre_nodes.push_back( prevNod[inode] );
4611 quantities.push_back( nbNodes );
4614 polyedre_nodes.push_back( nextNod[0] );
4615 for (int inode = nbNodes; inode-1; --inode )
4616 polyedre_nodes.push_back( nextNod[inode-1] );
4617 quantities.push_back( nbNodes );
4625 const int iQuad = elem->IsQuadratic();
4626 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4628 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4629 int inextface = (iface+1+iQuad) % nbNodes;
4630 int imid = (iface+1) % nbNodes;
4631 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4632 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4633 polyedre_nodes.push_back( prevNod[iface] ); // 1
4634 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4636 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4637 polyedre_nodes.push_back( nextNod[iface] ); // 2
4639 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4640 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4642 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4643 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4645 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4646 if ( nbFaceNodes > 2 )
4647 quantities.push_back( nbFaceNodes );
4648 else // degenerated face
4649 polyedre_nodes.resize( prevNbNodes );
4651 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4653 } // try to create a polyherdal prism
4656 newElems.push_back( aNewElem );
4657 myLastCreatedElems.push_back(aNewElem);
4658 srcElements.push_back( elem );
4661 // set new prev nodes
4662 for ( iNode = 0; iNode < nbNodes; iNode++ )
4663 prevNod[ iNode ] = nextNod[ iNode ];
4668 //=======================================================================
4670 * \brief Create 1D and 2D elements around swept elements
4671 * \param mapNewNodes - source nodes and ones generated from them
4672 * \param newElemsMap - source elements and ones generated from them
4673 * \param elemNewNodesMap - nodes generated from each node of each element
4674 * \param elemSet - all swept elements
4675 * \param nbSteps - number of sweeping steps
4676 * \param srcElements - to append elem for each generated element
4678 //=======================================================================
4680 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4681 TTElemOfElemListMap & newElemsMap,
4682 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4683 TIDSortedElemSet& elemSet,
4685 SMESH_SequenceOfElemPtr& srcElements)
4687 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4688 SMESHDS_Mesh* aMesh = GetMeshDS();
4690 // Find nodes belonging to only one initial element - sweep them into edges.
4692 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4693 for ( ; nList != mapNewNodes.end(); nList++ )
4695 const SMDS_MeshNode* node =
4696 static_cast<const SMDS_MeshNode*>( nList->first );
4697 if ( newElemsMap.count( node ))
4698 continue; // node was extruded into edge
4699 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4700 int nbInitElems = 0;
4701 const SMDS_MeshElement* el = 0;
4702 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4703 while ( eIt->more() && nbInitElems < 2 ) {
4704 const SMDS_MeshElement* e = eIt->next();
4705 SMDSAbs_ElementType type = e->GetType();
4706 if ( type == SMDSAbs_Volume ||
4710 if ( type > highType ) {
4717 if ( nbInitElems == 1 ) {
4718 bool NotCreateEdge = el && el->IsMediumNode(node);
4719 if(!NotCreateEdge) {
4720 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4721 list<const SMDS_MeshElement*> newEdges;
4722 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4727 // Make a ceiling for each element ie an equal element of last new nodes.
4728 // Find free links of faces - make edges and sweep them into faces.
4730 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4732 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4733 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4734 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4736 const SMDS_MeshElement* elem = itElem->first;
4737 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4739 if(itElem->second.size()==0) continue;
4741 const bool isQuadratic = elem->IsQuadratic();
4743 if ( elem->GetType() == SMDSAbs_Edge ) {
4744 // create a ceiling edge
4745 if ( !isQuadratic ) {
4746 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4747 vecNewNodes[ 1 ]->second.back())) {
4748 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4749 vecNewNodes[ 1 ]->second.back()));
4750 srcElements.push_back( elem );
4754 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4755 vecNewNodes[ 1 ]->second.back(),
4756 vecNewNodes[ 2 ]->second.back())) {
4757 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4758 vecNewNodes[ 1 ]->second.back(),
4759 vecNewNodes[ 2 ]->second.back()));
4760 srcElements.push_back( elem );
4764 if ( elem->GetType() != SMDSAbs_Face )
4767 bool hasFreeLinks = false;
4769 TIDSortedElemSet avoidSet;
4770 avoidSet.insert( elem );
4772 set<const SMDS_MeshNode*> aFaceLastNodes;
4773 int iNode, nbNodes = vecNewNodes.size();
4774 if ( !isQuadratic ) {
4775 // loop on the face nodes
4776 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4777 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778 // look for free links of the face
4779 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4780 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782 // check if a link n1-n2 is free
4783 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4784 hasFreeLinks = true;
4785 // make a new edge and a ceiling for a new edge
4786 const SMDS_MeshElement* edge;
4787 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4788 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4789 srcElements.push_back( myLastCreatedElems.back() );
4791 n1 = vecNewNodes[ iNode ]->second.back();
4792 n2 = vecNewNodes[ iNext ]->second.back();
4793 if ( !aMesh->FindEdge( n1, n2 )) {
4794 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4795 srcElements.push_back( edge );
4800 else { // elem is quadratic face
4801 int nbn = nbNodes/2;
4802 for ( iNode = 0; iNode < nbn; iNode++ ) {
4803 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4804 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4805 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4806 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4807 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4808 // check if a link is free
4809 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4810 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4811 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4812 hasFreeLinks = true;
4813 // make an edge and a ceiling for a new edge
4815 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4816 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4817 srcElements.push_back( elem );
4819 n1 = vecNewNodes[ iNode ]->second.back();
4820 n2 = vecNewNodes[ iNext ]->second.back();
4821 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4822 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4823 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4824 srcElements.push_back( elem );
4828 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4829 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4833 // sweep free links into faces
4835 if ( hasFreeLinks ) {
4836 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4837 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4839 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4840 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4841 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4842 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4843 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4845 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4846 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4847 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4849 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4850 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4851 std::advance( v, volNb );
4852 // find indices of free faces of a volume and their source edges
4853 list< int > freeInd;
4854 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4855 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4856 int iF, nbF = vTool.NbFaces();
4857 for ( iF = 0; iF < nbF; iF ++ ) {
4858 if ( vTool.IsFreeFace( iF ) &&
4859 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4860 initNodeSet != faceNodeSet) // except an initial face
4862 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4864 if ( faceNodeSet == initNodeSetNoCenter )
4866 freeInd.push_back( iF );
4867 // find source edge of a free face iF
4868 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4869 vector<const SMDS_MeshNode*>::iterator lastCommom;
4870 commonNodes.resize( nbNodes, 0 );
4871 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4872 initNodeSet.begin(), initNodeSet.end(),
4873 commonNodes.begin());
4874 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4875 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4877 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4879 if ( !srcEdges.back() )
4881 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4882 << iF << " of volume #" << vTool.ID() << endl;
4887 if ( freeInd.empty() )
4890 // create wall faces for all steps;
4891 // if such a face has been already created by sweep of edge,
4892 // assure that its orientation is OK
4893 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4895 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4896 vTool.SetExternalNormal();
4897 const int nextShift = vTool.IsForward() ? +1 : -1;
4898 list< int >::iterator ind = freeInd.begin();
4899 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4900 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4902 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4903 int nbn = vTool.NbFaceNodes( *ind );
4904 const SMDS_MeshElement * f = 0;
4905 if ( nbn == 3 ) ///// triangle
4907 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4909 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4911 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4913 nodes[ 1 + nextShift ] };
4915 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4917 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4921 else if ( nbn == 4 ) ///// quadrangle
4923 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4925 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4927 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4928 nodes[ 2 ], nodes[ 2+nextShift ] };
4930 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4933 newOrder[ 2 ], newOrder[ 3 ]));
4936 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4938 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4940 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4942 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4944 nodes[2 + 2*nextShift],
4945 nodes[3 - 2*nextShift],
4947 nodes[3 + 2*nextShift]};
4949 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4951 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4959 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4961 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4962 nodes[1], nodes[3], nodes[5], nodes[7] );
4964 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4967 nodes[4 - 2*nextShift],
4969 nodes[4 + 2*nextShift],
4971 nodes[5 - 2*nextShift],
4973 nodes[5 + 2*nextShift] };
4975 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4977 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978 newOrder[ 2 ], newOrder[ 3 ],
4979 newOrder[ 4 ], newOrder[ 5 ],
4980 newOrder[ 6 ], newOrder[ 7 ]));
4983 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4985 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4986 SMDSAbs_Face, /*noMedium=*/false);
4988 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4990 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4991 nodes[4 - 2*nextShift],
4993 nodes[4 + 2*nextShift],
4995 nodes[5 - 2*nextShift],
4997 nodes[5 + 2*nextShift],
5000 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5002 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5003 newOrder[ 2 ], newOrder[ 3 ],
5004 newOrder[ 4 ], newOrder[ 5 ],
5005 newOrder[ 6 ], newOrder[ 7 ],
5009 else //////// polygon
5011 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5012 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5014 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5016 if ( !vTool.IsForward() )
5017 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5019 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5021 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5025 while ( srcElements.size() < myLastCreatedElems.size() )
5026 srcElements.push_back( *srcEdge );
5028 } // loop on free faces
5030 // go to the next volume
5032 while ( iVol++ < nbVolumesByStep ) v++;
5035 } // loop on volumes of one step
5036 } // sweep free links into faces
5038 // Make a ceiling face with a normal external to a volume
5040 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5041 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5042 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5044 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5045 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5046 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5050 lastVol.SetExternalNormal();
5051 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5052 const int nbn = lastVol.NbFaceNodes( iF );
5053 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5054 if ( !hasFreeLinks ||
5055 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5057 const vector<int>& interlace =
5058 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5059 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5061 AddElement( nodeVec, anyFace.Init( elem ));
5063 while ( srcElements.size() < myLastCreatedElems.size() )
5064 srcElements.push_back( elem );
5067 } // loop on swept elements
5070 //=======================================================================
5071 //function : RotationSweep
5073 //=======================================================================
5075 SMESH_MeshEditor::PGroupIDs
5076 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5077 const gp_Ax1& theAxis,
5078 const double theAngle,
5079 const int theNbSteps,
5080 const double theTol,
5081 const bool theMakeGroups,
5082 const bool theMakeWalls)
5086 setElemsFirst( theElemSets );
5087 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5088 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5090 // source elements for each generated one
5091 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5092 srcElems.reserve( theElemSets[0].size() );
5093 srcNodes.reserve( theElemSets[1].size() );
5096 aTrsf.SetRotation( theAxis, theAngle );
5098 aTrsf2.SetRotation( theAxis, theAngle/2. );
5100 gp_Lin aLine( theAxis );
5101 double aSqTol = theTol * theTol;
5103 SMESHDS_Mesh* aMesh = GetMeshDS();
5105 TNodeOfNodeListMap mapNewNodes;
5106 TElemOfVecOfNnlmiMap mapElemNewNodes;
5107 TTElemOfElemListMap newElemsMap;
5109 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5110 myMesh->NbFaces(ORDER_QUADRATIC) +
5111 myMesh->NbVolumes(ORDER_QUADRATIC) );
5112 // loop on theElemSets
5113 TIDSortedElemSet::iterator itElem;
5114 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5116 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5117 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5118 const SMDS_MeshElement* elem = *itElem;
5119 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5121 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5122 newNodesItVec.reserve( elem->NbNodes() );
5124 // loop on elem nodes
5125 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5126 while ( itN->more() )
5128 const SMDS_MeshNode* node = cast2Node( itN->next() );
5130 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5132 aXYZ.Coord( coord[0], coord[1], coord[2] );
5133 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5135 // check if a node has been already sweeped
5136 TNodeOfNodeListMapItr nIt =
5137 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5138 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5139 if ( listNewNodes.empty() )
5141 // check if we are to create medium nodes between corner ones
5142 bool needMediumNodes = false;
5143 if ( isQuadraticMesh )
5145 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5146 while (it->more() && !needMediumNodes )
5148 const SMDS_MeshElement* invElem = it->next();
5149 if ( invElem != elem && !theElems.count( invElem )) continue;
5150 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5151 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5152 needMediumNodes = true;
5157 const SMDS_MeshNode * newNode = node;
5158 for ( int i = 0; i < theNbSteps; i++ ) {
5160 if ( needMediumNodes ) // create a medium node
5162 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5163 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5164 myLastCreatedNodes.push_back(newNode);
5165 srcNodes.push_back( node );
5166 listNewNodes.push_back( newNode );
5167 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5170 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5172 // create a corner node
5173 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5174 myLastCreatedNodes.push_back(newNode);
5175 srcNodes.push_back( node );
5176 listNewNodes.push_back( newNode );
5179 listNewNodes.push_back( newNode );
5180 // if ( needMediumNodes )
5181 // listNewNodes.push_back( newNode );
5185 newNodesItVec.push_back( nIt );
5187 // make new elements
5188 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5193 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5195 PGroupIDs newGroupIDs;
5196 if ( theMakeGroups )
5197 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5202 //=======================================================================
5203 //function : ExtrusParam
5204 //purpose : standard construction
5205 //=======================================================================
5207 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5208 const int theNbSteps,
5209 const std::list<double>& theScales,
5210 const std::list<double>& theAngles,
5211 const gp_XYZ* theBasePoint,
5213 const double theTolerance):
5215 myBaseP( Precision::Infinite(), 0, 0 ),
5216 myFlags( theFlags ),
5217 myTolerance( theTolerance ),
5218 myElemsToUse( NULL )
5220 mySteps = new TColStd_HSequenceOfReal;
5221 const double stepSize = theStep.Magnitude();
5222 for (int i=1; i<=theNbSteps; i++ )
5223 mySteps->Append( stepSize );
5225 if ( !theScales.empty() )
5227 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5228 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5230 // add medium scales
5231 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5232 myScales.reserve( theNbSteps * 2 );
5233 myScales.push_back( 0.5 * ( *s1 + 1. ));
5234 myScales.push_back( *s1 );
5235 for ( ; s2 != theScales.end(); s1 = s2++ )
5237 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5238 myScales.push_back( *s2 );
5242 if ( !theAngles.empty() )
5244 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5245 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5246 linearAngleVariation( theNbSteps, angles );
5248 // accumulate angles
5251 std::list<double>::iterator a1 = angles.begin(), a2;
5252 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5257 while ( nbAngles++ < theNbSteps )
5258 angles.push_back( angles.back() );
5260 // add medium angles
5261 a2 = angles.begin(), a1 = a2++;
5262 myAngles.push_back( 0.5 * *a1 );
5263 myAngles.push_back( *a1 );
5264 for ( ; a2 != angles.end(); a1 = a2++ )
5266 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5267 myAngles.push_back( *a2 );
5273 myBaseP = *theBasePoint;
5276 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5277 ( theTolerance > 0 ))
5279 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5283 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5287 //=======================================================================
5288 //function : ExtrusParam
5289 //purpose : steps are given explicitly
5290 //=======================================================================
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5293 Handle(TColStd_HSequenceOfReal) theSteps,
5295 const double theTolerance):
5297 mySteps( theSteps ),
5298 myFlags( theFlags ),
5299 myTolerance( theTolerance ),
5300 myElemsToUse( NULL )
5302 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5303 ( theTolerance > 0 ))
5305 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5309 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5313 //=======================================================================
5314 //function : ExtrusParam
5315 //purpose : for extrusion by normal
5316 //=======================================================================
5318 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5319 const int theNbSteps,
5323 mySteps( new TColStd_HSequenceOfReal ),
5324 myFlags( theFlags ),
5326 myElemsToUse( NULL )
5328 for (int i = 0; i < theNbSteps; i++ )
5329 mySteps->Append( theStepSize );
5333 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5337 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5341 //=======================================================================
5342 //function : ExtrusParam
5343 //purpose : for extrusion along path
5344 //=======================================================================
5346 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5347 const gp_Pnt* theBasePoint,
5348 const std::list<double>& theScales,
5349 const bool theMakeGroups )
5350 : myBaseP( Precision::Infinite(), 0, 0 ),
5351 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5352 myPathPoints( thePoints )
5356 myBaseP = theBasePoint->XYZ();
5359 if ( !theScales.empty() )
5361 // add medium scales
5362 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5363 myScales.reserve( thePoints.size() * 2 );
5364 myScales.push_back( 0.5 * ( 1. + *s1 ));
5365 myScales.push_back( *s1 );
5366 for ( ; s2 != theScales.end(); s1 = s2++ )
5368 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5369 myScales.push_back( *s2 );
5373 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5376 //=======================================================================
5377 //function : ExtrusParam::SetElementsToUse
5378 //purpose : stores elements to use for extrusion by normal, depending on
5379 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5380 // define myBaseP for scaling
5381 //=======================================================================
5383 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5384 const TIDSortedElemSet& nodes )
5386 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5388 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5390 myBaseP.SetCoord( 0.,0.,0. );
5391 TIDSortedElemSet newNodes;
5393 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5394 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5396 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5397 TIDSortedElemSet::const_iterator itElem = elements.begin();
5398 for ( ; itElem != elements.end(); itElem++ )
5400 const SMDS_MeshElement* elem = *itElem;
5401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402 while ( itN->more() ) {
5403 const SMDS_MeshElement* node = itN->next();
5404 if ( newNodes.insert( node ).second )
5405 myBaseP += SMESH_NodeXYZ( node );
5409 myBaseP /= newNodes.size();
5413 //=======================================================================
5414 //function : ExtrusParam::beginStepIter
5415 //purpose : prepare iteration on steps
5416 //=======================================================================
5418 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5420 myWithMediumNodes = withMediumNodes;
5424 //=======================================================================
5425 //function : ExtrusParam::moreSteps
5426 //purpose : are there more steps?
5427 //=======================================================================
5429 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5431 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5433 //=======================================================================
5434 //function : ExtrusParam::nextStep
5435 //purpose : returns the next step
5436 //=======================================================================
5438 double SMESH_MeshEditor::ExtrusParam::nextStep()
5441 if ( !myCurSteps.empty() )
5443 res = myCurSteps.back();
5444 myCurSteps.pop_back();
5446 else if ( myNextStep <= mySteps->Length() )
5448 myCurSteps.push_back( mySteps->Value( myNextStep ));
5450 if ( myWithMediumNodes )
5452 myCurSteps.back() /= 2.;
5453 myCurSteps.push_back( myCurSteps.back() );
5460 //=======================================================================
5461 //function : ExtrusParam::makeNodesByDir
5462 //purpose : create nodes for standard extrusion
5463 //=======================================================================
5465 int SMESH_MeshEditor::ExtrusParam::
5466 makeNodesByDir( SMESHDS_Mesh* mesh,
5467 const SMDS_MeshNode* srcNode,
5468 std::list<const SMDS_MeshNode*> & newNodes,
5469 const bool makeMediumNodes)
5471 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5474 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5476 p += myDir.XYZ() * nextStep();
5477 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5478 newNodes.push_back( newNode );
5481 if ( !myScales.empty() || !myAngles.empty() )
5483 gp_XYZ center = myBaseP;
5484 gp_Ax1 ratationAxis( center, myDir );
5487 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5488 size_t i = !makeMediumNodes;
5489 for ( beginStepIter( makeMediumNodes );
5491 ++nIt, i += 1 + !makeMediumNodes )
5493 center += myDir.XYZ() * nextStep();
5495 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5497 if ( i < myScales.size() )
5499 xyz = ( myScales[i] * ( xyz - center )) + center;
5502 if ( !myAngles.empty() )
5504 rotation.SetRotation( ratationAxis, myAngles[i] );
5505 rotation.Transforms( xyz );
5509 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5517 //=======================================================================
5518 //function : ExtrusParam::makeNodesByDirAndSew
5519 //purpose : create nodes for standard extrusion with sewing
5520 //=======================================================================
5522 int SMESH_MeshEditor::ExtrusParam::
5523 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5524 const SMDS_MeshNode* srcNode,
5525 std::list<const SMDS_MeshNode*> & newNodes,
5526 const bool makeMediumNodes)
5528 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5531 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5533 P1 += myDir.XYZ() * nextStep();
5535 // try to search in sequence of existing nodes
5536 // if myNodes.size()>0 we 'nave to use given sequence
5537 // else - use all nodes of mesh
5538 const SMDS_MeshNode * node = 0;
5539 if ( myNodes.Length() > 0 )
5541 for ( int i = 1; i <= myNodes.Length(); i++ )
5543 SMESH_NodeXYZ P2 = myNodes.Value(i);
5544 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5546 node = myNodes.Value(i);
5553 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5556 SMESH_NodeXYZ P2 = itn->next();
5557 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5566 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5568 newNodes.push_back( node );
5575 //=======================================================================
5576 //function : ExtrusParam::makeNodesByNormal2D
5577 //purpose : create nodes for extrusion using normals of faces
5578 //=======================================================================
5580 int SMESH_MeshEditor::ExtrusParam::
5581 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5582 const SMDS_MeshNode* srcNode,
5583 std::list<const SMDS_MeshNode*> & newNodes,
5584 const bool makeMediumNodes)
5586 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5588 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5590 // get normals to faces sharing srcNode
5591 vector< gp_XYZ > norms, baryCenters;
5592 gp_XYZ norm, avgNorm( 0,0,0 );
5593 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5594 while ( faceIt->more() )
5596 const SMDS_MeshElement* face = faceIt->next();
5597 if ( myElemsToUse && !myElemsToUse->count( face ))
5599 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5601 norms.push_back( norm );
5603 if ( !alongAvgNorm )
5607 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5608 bc += SMESH_NodeXYZ( nIt->next() );
5609 baryCenters.push_back( bc / nbN );
5614 if ( norms.empty() ) return 0;
5616 double normSize = avgNorm.Modulus();
5617 if ( normSize < std::numeric_limits<double>::min() )
5620 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5623 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5626 avgNorm /= normSize;
5629 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5632 double stepSize = nextStep();
5634 if ( norms.size() > 1 )
5636 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5638 // translate plane of a face
5639 baryCenters[ iF ] += norms[ iF ] * stepSize;
5641 // find point of intersection of the face plane located at baryCenters[ iF ]
5642 // and avgNorm located at pNew
5643 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5644 double dot = ( norms[ iF ] * avgNorm );
5645 if ( dot < std::numeric_limits<double>::min() )
5646 dot = stepSize * 1e-3;
5647 double step = -( norms[ iF ] * pNew + d ) / dot;
5648 pNew += step * avgNorm;
5653 pNew += stepSize * avgNorm;
5657 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658 newNodes.push_back( newNode );
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByNormal1D
5665 //purpose : create nodes for extrusion using normals of edges
5666 //=======================================================================
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5670 const SMDS_MeshNode* srcNode,
5671 std::list<const SMDS_MeshNode*> & newNodes,
5672 const bool makeMediumNodes)
5674 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesAlongTrack
5680 //purpose : create nodes for extrusion along path
5681 //=======================================================================
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5685 const SMDS_MeshNode* srcNode,
5686 std::list<const SMDS_MeshNode*> & newNodes,
5687 const bool makeMediumNodes)
5689 const Standard_Real aTolAng=1.e-4;
5691 gp_Pnt aV0x = myBaseP;
5692 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5694 const PathPoint& aPP0 = myPathPoints[0];
5695 gp_Pnt aP0x = aPP0.myPnt;
5696 gp_Dir aDT0x= aPP0.myTgt;
5698 std::vector< gp_Pnt > centers;
5699 centers.reserve( NbSteps() * 2 );
5701 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5703 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5705 const PathPoint& aPP = myPathPoints[j];
5706 const gp_Pnt& aP1x = aPP.myPnt;
5707 const gp_Dir& aDT1x = aPP.myTgt;
5710 gp_Vec aV01x( aP0x, aP1x );
5711 aTrsf.SetTranslation( aV01x );
5712 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5713 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5715 // rotation 1 [ T1,T0 ]
5716 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5717 if ( fabs( aAngleT1T0 ) > aTolAng )
5719 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5720 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5722 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5726 if ( aPP.myAngle != 0. )
5728 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5729 aPN1 = aPN1.Transformed( aTrsfRot );
5733 if ( makeMediumNodes )
5735 // create additional node
5736 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5737 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5738 newNodes.push_back( newNode );
5741 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5742 newNodes.push_back( newNode );
5744 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5745 centers.push_back( aV1x );
5754 if ( !myScales.empty() )
5757 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5758 for ( size_t i = !makeMediumNodes;
5759 i < myScales.size() && node != newNodes.end();
5760 i += ( 1 + !makeMediumNodes ), ++node )
5762 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5763 gp_Pnt aN = SMESH_NodeXYZ( *node );
5764 gp_Pnt aP = aN.Transformed( aTrsfScale );
5765 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5769 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5772 //=======================================================================
5773 //function : ExtrusionSweep
5775 //=======================================================================
5777 SMESH_MeshEditor::PGroupIDs
5778 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5779 const gp_Vec& theStep,
5780 const int theNbSteps,
5781 TTElemOfElemListMap& newElemsMap,
5783 const double theTolerance)
5785 std::list<double> dummy;
5786 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5787 theFlags, theTolerance );
5788 return ExtrusionSweep( theElems, aParams, newElemsMap );
5794 //=======================================================================
5795 //function : getOriFactor
5796 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5797 // edge curve orientation
5798 //=======================================================================
5800 double getOriFactor( const TopoDS_Edge& edge,
5801 const SMDS_MeshNode* n1,
5802 const SMDS_MeshNode* n2,
5803 SMESH_MesherHelper& helper)
5805 double u1 = helper.GetNodeU( edge, n1, n2 );
5806 double u2 = helper.GetNodeU( edge, n2, n1 );
5807 return u1 < u2 ? 1. : -1.;
5811 //=======================================================================
5812 //function : ExtrusionSweep
5814 //=======================================================================
5816 SMESH_MeshEditor::PGroupIDs
5817 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5818 ExtrusParam& theParams,
5819 TTElemOfElemListMap& newElemsMap)
5823 setElemsFirst( theElemSets );
5824 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5825 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5827 // source elements for each generated one
5828 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5829 srcElems.reserve( theElemSets[0].size() );
5830 srcNodes.reserve( theElemSets[1].size() );
5832 const int nbSteps = theParams.NbSteps();
5833 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5835 TNodeOfNodeListMap mapNewNodes;
5836 TElemOfVecOfNnlmiMap mapElemNewNodes;
5838 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5839 myMesh->NbFaces(ORDER_QUADRATIC) +
5840 myMesh->NbVolumes(ORDER_QUADRATIC) );
5842 TIDSortedElemSet::iterator itElem;
5843 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5845 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5846 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5848 // check element type
5849 const SMDS_MeshElement* elem = *itElem;
5850 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5853 const size_t nbNodes = elem->NbNodes();
5854 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5855 newNodesItVec.reserve( nbNodes );
5857 // loop on elem nodes
5858 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5859 while ( itN->more() )
5861 // check if a node has been already sweeped
5862 const SMDS_MeshNode* node = itN->next();
5863 TNodeOfNodeListMap::iterator nIt =
5864 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5865 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5866 if ( listNewNodes.empty() )
5870 // check if we are to create medium nodes between corner ones
5871 bool needMediumNodes = false;
5872 if ( isQuadraticMesh )
5874 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5875 while (it->more() && !needMediumNodes )
5877 const SMDS_MeshElement* invElem = it->next();
5878 if ( invElem != elem && !theElems.count( invElem )) continue;
5879 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5880 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5881 needMediumNodes = true;
5884 // create nodes for all steps
5885 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5887 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5888 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5890 myLastCreatedNodes.push_back( *newNodesIt );
5891 srcNodes.push_back( node );
5896 if ( theParams.ToMakeBoundary() )
5898 GetMeshDS()->Modified();
5899 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5901 break; // newNodesItVec will be shorter than nbNodes
5904 newNodesItVec.push_back( nIt );
5906 // make new elements
5907 if ( newNodesItVec.size() == nbNodes )
5908 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5912 if ( theParams.ToMakeBoundary() ) {
5913 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5915 PGroupIDs newGroupIDs;
5916 if ( theParams.ToMakeGroups() )
5917 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5922 //=======================================================================
5923 //function : ExtrusionAlongTrack
5925 //=======================================================================
5926 SMESH_MeshEditor::Extrusion_Error
5927 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5928 SMESH_Mesh* theTrackMesh,
5929 SMDS_ElemIteratorPtr theTrackIterator,
5930 const SMDS_MeshNode* theN1,
5931 std::list<double>& theAngles,
5932 const bool theAngleVariation,
5933 std::list<double>& theScales,
5934 const bool theScaleVariation,
5935 const gp_Pnt* theRefPoint,
5936 const bool theMakeGroups)
5941 if ( theElements[0].empty() && theElements[1].empty() )
5942 return EXTR_NO_ELEMENTS;
5944 ASSERT( theTrackMesh );
5945 if ( ! theTrackIterator || !theTrackIterator->more() )
5946 return EXTR_NO_ELEMENTS;
5948 // 2. Get ordered nodes
5949 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5950 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5951 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5952 if ( branchEdges.empty() )
5953 return EXTR_PATH_NOT_EDGE;
5955 if ( branchEdges.size() > 1 )
5956 return EXTR_BAD_PATH_SHAPE;
5958 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5959 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5960 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5961 return EXTR_BAD_STARTING_NODE;
5963 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5965 // add medium nodes to pathNodes
5966 std::vector< const SMDS_MeshNode* > pathNodes2;
5967 std::vector< const SMDS_MeshElement* > pathEdges2;
5968 pathNodes2.reserve( pathNodes.size() * 2 );
5969 pathEdges2.reserve( pathEdges.size() * 2 );
5970 for ( size_t i = 0; i < pathEdges.size(); ++i )
5972 pathNodes2.push_back( pathNodes[i] );
5973 pathEdges2.push_back( pathEdges[i] );
5974 if ( pathEdges[i]->IsQuadratic() )
5976 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5977 pathEdges2.push_back( pathEdges[i] );
5980 pathNodes2.push_back( pathNodes.back() );
5981 pathEdges.swap( pathEdges2 );
5982 pathNodes.swap( pathNodes2 );
5985 // 3. Get path data at pathNodes
5987 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5989 if ( theAngleVariation )
5990 linearAngleVariation( points.size()-1, theAngles );
5991 if ( theScaleVariation )
5992 linearScaleVariation( points.size()-1, theScales );
5994 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5995 std::list<double>::iterator angle = theAngles.begin();
5997 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5999 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6000 std::map< int, double >::iterator id2factor;
6001 SMESH_MesherHelper pathHelper( *theTrackMesh );
6002 gp_Pnt p; gp_Vec tangent;
6003 const double tol2 = gp::Resolution() * gp::Resolution();
6005 for ( size_t i = 0; i < pathNodes.size(); ++i )
6007 ExtrusParam::PathPoint & point = points[ i ];
6009 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6011 if ( angle != theAngles.end() )
6012 point.myAngle = *angle++;
6014 tangent.SetCoord( 0,0,0 );
6015 const int shapeID = pathNodes[ i ]->GetShapeID();
6016 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6017 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6018 switch ( shapeType )
6022 TopoDS_Edge edge = TopoDS::Edge( shape );
6023 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6024 if ( id2factor->second == 0 )
6026 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6027 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6029 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6030 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6031 curve->D1( u, p, tangent );
6032 tangent *= id2factor->second;
6038 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6039 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6041 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6042 for ( int di = -1; di <= 0; ++di )
6045 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6047 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6048 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6049 if ( id2factor->second == 0 )
6052 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6054 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6056 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6057 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6059 curve->D1( u, p, du );
6060 double size2 = du.SquareMagnitude();
6061 if ( du.SquareMagnitude() > tol2 )
6063 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6075 for ( int di = -1; di <= 1; di += 2 )
6078 if ( j < pathNodes.size() )
6080 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6081 double size2 = dir.SquareMagnitude();
6083 tangent += dir.Divided( Sqrt( size2 )) * di;
6087 } // switch ( shapeType )
6089 if ( tangent.SquareMagnitude() < tol2 )
6090 return EXTR_CANT_GET_TANGENT;
6092 point.myTgt = tangent;
6094 } // loop on pathNodes
6097 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6098 TTElemOfElemListMap newElemsMap;
6100 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6105 //=======================================================================
6106 //function : linearAngleVariation
6107 //purpose : spread values over nbSteps
6108 //=======================================================================
6110 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6111 list<double>& Angles)
6113 int nbAngles = Angles.size();
6114 if( nbSteps > nbAngles && nbAngles > 0 )
6116 vector<double> theAngles(nbAngles);
6117 theAngles.assign( Angles.begin(), Angles.end() );
6120 double rAn2St = double( nbAngles ) / double( nbSteps );
6121 double angPrev = 0, angle;
6122 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6124 double angCur = rAn2St * ( iSt+1 );
6125 double angCurFloor = floor( angCur );
6126 double angPrevFloor = floor( angPrev );
6127 if ( angPrevFloor == angCurFloor )
6128 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6130 int iP = int( angPrevFloor );
6131 double angPrevCeil = ceil(angPrev);
6132 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6134 int iC = int( angCurFloor );
6135 if ( iC < nbAngles )
6136 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6138 iP = int( angPrevCeil );
6140 angle += theAngles[ iC ];
6142 res.push_back(angle);
6149 //=======================================================================
6150 //function : linearScaleVariation
6151 //purpose : spread values over nbSteps
6152 //=======================================================================
6154 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6155 std::list<double>& theScales)
6157 int nbScales = theScales.size();
6158 std::vector<double> myScales;
6159 myScales.reserve( theNbSteps );
6160 std::list<double>::const_iterator scale = theScales.begin();
6161 double prevScale = 1.0;
6162 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6164 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6165 int stDelta = Max( 1, iStep - myScales.size());
6166 double scDelta = ( *scale - prevScale ) / stDelta;
6167 for ( int iStep = 0; iStep < stDelta; ++iStep )
6169 myScales.push_back( prevScale + scDelta );
6170 prevScale = myScales.back();
6174 theScales.assign( myScales.begin(), myScales.end() );
6177 //================================================================================
6179 * \brief Move or copy theElements applying theTrsf to their nodes
6180 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6181 * \param theTrsf - transformation to apply
6182 * \param theCopy - if true, create translated copies of theElems
6183 * \param theMakeGroups - if true and theCopy, create translated groups
6184 * \param theTargetMesh - mesh to copy translated elements into
6185 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6187 //================================================================================
6189 SMESH_MeshEditor::PGroupIDs
6190 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6191 const gp_Trsf& theTrsf,
6193 const bool theMakeGroups,
6194 SMESH_Mesh* theTargetMesh)
6197 myLastCreatedElems.reserve( theElems.size() );
6199 bool needReverse = false;
6200 string groupPostfix;
6201 switch ( theTrsf.Form() ) {
6204 groupPostfix = "mirrored";
6207 groupPostfix = "mirrored";
6211 groupPostfix = "mirrored";
6214 groupPostfix = "rotated";
6216 case gp_Translation:
6217 groupPostfix = "translated";
6220 groupPostfix = "scaled";
6222 case gp_CompoundTrsf: // different scale by axis
6223 groupPostfix = "scaled";
6226 needReverse = false;
6227 groupPostfix = "transformed";
6230 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6231 SMESHDS_Mesh* aMesh = GetMeshDS();
6233 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6234 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6235 SMESH_MeshEditor::ElemFeatures elemType;
6237 // map old node to new one
6238 TNodeNodeMap nodeMap;
6240 // elements sharing moved nodes; those of them which have all
6241 // nodes mirrored but are not in theElems are to be reversed
6242 TIDSortedElemSet inverseElemSet;
6244 // source elements for each generated one
6245 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6247 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6248 TIDSortedElemSet orphanNode;
6250 if ( theElems.empty() ) // transform the whole mesh
6253 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6254 while ( eIt->more() ) theElems.insert( eIt->next() );
6256 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6257 while ( nIt->more() )
6259 const SMDS_MeshNode* node = nIt->next();
6260 if ( node->NbInverseElements() == 0)
6261 orphanNode.insert( node );
6265 // loop on elements to transform nodes : first orphan nodes then elems
6266 TIDSortedElemSet::iterator itElem;
6267 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6268 for (int i=0; i<2; i++)
6269 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6271 const SMDS_MeshElement* elem = *itElem;
6275 // loop on elem nodes
6277 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6278 while ( itN->more() )
6280 const SMDS_MeshNode* node = cast2Node( itN->next() );
6281 // check if a node has been already transformed
6282 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6283 nodeMap.insert( make_pair ( node, node ));
6284 if ( !n2n_isnew.second )
6287 node->GetXYZ( coord );
6288 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6289 if ( theTargetMesh ) {
6290 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6291 n2n_isnew.first->second = newNode;
6292 myLastCreatedNodes.push_back(newNode);
6293 srcNodes.push_back( node );
6295 else if ( theCopy ) {
6296 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6297 n2n_isnew.first->second = newNode;
6298 myLastCreatedNodes.push_back(newNode);
6299 srcNodes.push_back( node );
6302 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6303 // node position on shape becomes invalid
6304 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6305 ( SMDS_SpacePosition::originSpacePosition() );
6308 // keep inverse elements
6309 if ( !theCopy && !theTargetMesh && needReverse ) {
6310 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6311 while ( invElemIt->more() ) {
6312 const SMDS_MeshElement* iel = invElemIt->next();
6313 inverseElemSet.insert( iel );
6317 } // loop on elems in { &orphanNode, &theElems };
6319 // either create new elements or reverse mirrored ones
6320 if ( !theCopy && !needReverse && !theTargetMesh )
6323 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6325 // Replicate or reverse elements
6327 std::vector<int> iForw;
6328 vector<const SMDS_MeshNode*> nodes;
6329 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6331 const SMDS_MeshElement* elem = *itElem;
6332 if ( !elem ) continue;
6334 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6335 size_t nbNodes = elem->NbNodes();
6336 if ( geomType == SMDSGeom_NONE ) continue; // node
6338 nodes.resize( nbNodes );
6340 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6342 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6346 bool allTransformed = true;
6347 int nbFaces = aPolyedre->NbFaces();
6348 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6350 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6351 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6353 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6354 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6355 if ( nodeMapIt == nodeMap.end() )
6356 allTransformed = false; // not all nodes transformed
6358 nodes.push_back((*nodeMapIt).second);
6360 if ( needReverse && allTransformed )
6361 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6363 if ( !allTransformed )
6364 continue; // not all nodes transformed
6366 else // ----------------------- the rest element types
6368 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6369 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6370 const vector<int>& i = needReverse ? iRev : iForw;
6372 // find transformed nodes
6374 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6375 while ( itN->more() ) {
6376 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6377 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6378 if ( nodeMapIt == nodeMap.end() )
6379 break; // not all nodes transformed
6380 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6382 if ( iNode != nbNodes )
6383 continue; // not all nodes transformed
6387 // copy in this or a new mesh
6388 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6389 srcElems.push_back( elem );
6392 // reverse element as it was reversed by transformation
6394 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6397 } // loop on elements
6399 if ( editor && editor != this )
6400 myLastCreatedElems.swap( editor->myLastCreatedElems );
6402 PGroupIDs newGroupIDs;
6404 if ( ( theMakeGroups && theCopy ) ||
6405 ( theMakeGroups && theTargetMesh ) )
6406 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6411 //================================================================================
6413 * \brief Make an offset mesh from a source 2D mesh
6414 * \param [in] theElements - source faces
6415 * \param [in] theValue - offset value
6416 * \param [out] theTgtMesh - a mesh to add offset elements to
6417 * \param [in] theMakeGroups - to generate groups
6418 * \return PGroupIDs - IDs of created groups. NULL means failure
6420 //================================================================================
6422 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6423 const double theValue,
6424 SMESH_Mesh* theTgtMesh,
6425 const bool theMakeGroups,
6426 const bool theCopyElements,
6427 const bool theFixSelfIntersection)
6429 SMESHDS_Mesh* meshDS = GetMeshDS();
6430 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6431 SMESH_MeshEditor tgtEditor( theTgtMesh );
6433 SMDS_ElemIteratorPtr eIt;
6434 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6435 else eIt = SMESHUtils::elemSetIterator( theElements );
6437 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6438 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6439 std::unique_ptr< SMDS_Mesh > offsetMesh
6440 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6441 theFixSelfIntersection,
6442 new2OldFaces, new2OldNodes ));
6443 if ( offsetMesh->NbElements() == 0 )
6444 return PGroupIDs(); // MakeOffset() failed
6447 if ( theTgtMesh == myMesh && !theCopyElements )
6449 // clear the source elements
6450 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451 else eIt = SMESHUtils::elemSetIterator( theElements );
6452 while ( eIt->more() )
6453 meshDS->RemoveFreeElement( eIt->next(), 0 );
6456 // offsetMesh->Modified();
6457 // offsetMesh->CompactMesh(); // make IDs start from 1
6459 // source elements for each generated one
6460 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6461 srcElems.reserve( new2OldFaces.size() );
6462 srcNodes.reserve( new2OldNodes.size() );
6465 myLastCreatedElems.reserve( new2OldFaces.size() );
6466 myLastCreatedNodes.reserve( new2OldNodes.size() );
6468 // copy offsetMesh to theTgtMesh
6470 int idShift = meshDS->MaxNodeID();
6471 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6472 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6475 if ( n->NbInverseElements() > 0 )
6478 const SMDS_MeshNode* n2 =
6479 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6480 myLastCreatedNodes.push_back( n2 );
6481 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6485 ElemFeatures elemType;
6486 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6487 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6490 elemType.myNodes.clear();
6491 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6493 const SMDS_MeshNode* n2 = nIt->next();
6494 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6496 tgtEditor.AddElement( elemType.myNodes, elemType );
6497 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6500 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6502 PGroupIDs newGroupIDs;
6503 if ( theMakeGroups )
6504 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6506 newGroupIDs.reset( new std::list< int > );
6511 //=======================================================================
6513 * \brief Create groups of elements made during transformation
6514 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6515 * \param elemGens - elements making corresponding myLastCreatedElems
6516 * \param postfix - to push_back to names of new groups
6517 * \param targetMesh - mesh to create groups in
6518 * \param topPresent - is there are "top" elements that are created by sweeping
6520 //=======================================================================
6522 SMESH_MeshEditor::PGroupIDs
6523 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6524 const SMESH_SequenceOfElemPtr& elemGens,
6525 const std::string& postfix,
6526 SMESH_Mesh* targetMesh,
6527 const bool topPresent)
6529 PGroupIDs newGroupIDs( new list<int> );
6530 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6532 // Sort existing groups by types and collect their names
6534 // containers to store an old group and generated new ones;
6535 // 1st new group is for result elems of different type than a source one;
6536 // 2nd new group is for same type result elems ("top" group at extrusion)
6538 using boost::make_tuple;
6539 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6540 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6541 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6543 set< string > groupNames;
6545 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6546 if ( !groupIt->more() ) return newGroupIDs;
6548 int newGroupID = mesh->GetGroupIds().back()+1;
6549 while ( groupIt->more() )
6551 SMESH_Group * group = groupIt->next();
6552 if ( !group ) continue;
6553 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6554 if ( !groupDS || groupDS->IsEmpty() ) continue;
6555 groupNames.insert ( group->GetName() );
6556 groupDS->SetStoreName( group->GetName() );
6557 const SMDSAbs_ElementType type = groupDS->GetType();
6558 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6559 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6560 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6561 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6564 // Loop on nodes and elements to add them in new groups
6566 vector< const SMDS_MeshElement* > resultElems;
6567 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6569 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6570 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6571 if ( gens.size() != elems.size() )
6572 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6574 // loop on created elements
6575 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6577 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6578 if ( !sourceElem ) {
6579 MESSAGE("generateGroups(): NULL source element");
6582 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6583 if ( groupsOldNew.empty() ) { // no groups of this type at all
6584 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6585 ++iElem; // skip all elements made by sourceElem
6588 // collect all elements made by the iElem-th sourceElem
6589 resultElems.clear();
6590 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6591 if ( resElem != sourceElem )
6592 resultElems.push_back( resElem );
6593 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6594 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6595 if ( resElem != sourceElem )
6596 resultElems.push_back( resElem );
6598 const SMDS_MeshElement* topElem = 0;
6599 if ( isNodes ) // there must be a top element
6601 topElem = resultElems.back();
6602 resultElems.pop_back();
6606 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6607 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6608 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6610 topElem = *resElemIt;
6611 *resElemIt = 0; // erase *resElemIt
6615 // add resultElems to groups originted from ones the sourceElem belongs to
6616 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6617 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6619 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6620 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6622 // fill in a new group
6623 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6624 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6625 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6627 newGroup.Add( *resElemIt );
6629 // fill a "top" group
6632 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6633 newTopGroup.Add( topElem );
6637 } // loop on created elements
6638 }// loop on nodes and elements
6640 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6642 list<int> topGrouIds;
6643 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6645 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6646 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6647 orderedOldNewGroups[i]->get<2>() };
6648 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6650 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6651 if ( newGroupDS->IsEmpty() )
6653 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6658 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6661 const bool isTop = ( topPresent &&
6662 newGroupDS->GetType() == oldGroupDS->GetType() &&
6665 string name = oldGroupDS->GetStoreName();
6666 { // remove trailing whitespaces (issue 22599)
6667 size_t size = name.size();
6668 while ( size > 1 && isspace( name[ size-1 ]))
6670 if ( size != name.size() )
6672 name.resize( size );
6673 oldGroupDS->SetStoreName( name.c_str() );
6676 if ( !targetMesh ) {
6677 string suffix = ( isTop ? "top": postfix.c_str() );
6681 while ( !groupNames.insert( name ).second ) // name exists
6682 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6687 newGroupDS->SetStoreName( name.c_str() );
6689 // make a SMESH_Groups
6690 mesh->AddGroup( newGroupDS );
6692 topGrouIds.push_back( newGroupDS->GetID() );
6694 newGroupIDs->push_back( newGroupDS->GetID() );
6698 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6703 //================================================================================
6705 * * \brief Return list of group of nodes close to each other within theTolerance
6706 * * Search among theNodes or in the whole mesh if theNodes is empty using
6707 * * an Octree algorithm
6708 * \param [in,out] theNodes - the nodes to treat
6709 * \param [in] theTolerance - the tolerance
6710 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6711 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6712 * corner and medium nodes in separate groups
6714 //================================================================================
6716 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6717 const double theTolerance,
6718 TListOfListOfNodes & theGroupsOfNodes,
6719 bool theSeparateCornersAndMedium)
6723 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6724 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6725 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6726 theSeparateCornersAndMedium = false;
6728 TIDSortedNodeSet& corners = theNodes;
6729 TIDSortedNodeSet medium;
6731 if ( theNodes.empty() ) // get all nodes in the mesh
6733 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6734 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6735 if ( theSeparateCornersAndMedium )
6736 while ( nIt->more() )
6738 const SMDS_MeshNode* n = nIt->next();
6739 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6740 nodeSet->insert( nodeSet->end(), n );
6743 while ( nIt->more() )
6744 theNodes.insert( theNodes.end(), nIt->next() );
6746 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6748 TIDSortedNodeSet::iterator nIt = corners.begin();
6749 while ( nIt != corners.end() )
6750 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6752 medium.insert( medium.end(), *nIt );
6753 corners.erase( nIt++ );
6761 if ( !corners.empty() )
6762 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6763 if ( !medium.empty() )
6764 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6767 //=======================================================================
6768 //function : SimplifyFace
6769 //purpose : split a chain of nodes into several closed chains
6770 //=======================================================================
6772 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6773 vector<const SMDS_MeshNode *>& poly_nodes,
6774 vector<int>& quantities) const
6776 int nbNodes = faceNodes.size();
6777 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6781 size_t prevNbQuant = quantities.size();
6783 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6784 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6785 map< const SMDS_MeshNode*, int >::iterator nInd;
6787 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6788 simpleNodes.push_back( faceNodes[0] );
6789 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6791 if ( faceNodes[ iCur ] != simpleNodes.back() )
6793 int index = simpleNodes.size();
6794 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6795 int prevIndex = nInd->second;
6796 if ( prevIndex < index )
6799 int loopLen = index - prevIndex;
6802 // store the sub-loop
6803 quantities.push_back( loopLen );
6804 for ( int i = prevIndex; i < index; i++ )
6805 poly_nodes.push_back( simpleNodes[ i ]);
6807 simpleNodes.resize( prevIndex+1 );
6811 simpleNodes.push_back( faceNodes[ iCur ]);
6816 if ( simpleNodes.size() > 2 )
6818 quantities.push_back( simpleNodes.size() );
6819 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6822 return quantities.size() - prevNbQuant;
6825 //=======================================================================
6826 //function : MergeNodes
6827 //purpose : In each group, the cdr of nodes are substituted by the first one
6829 //=======================================================================
6831 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6832 const bool theAvoidMakingHoles)
6836 SMESHDS_Mesh* mesh = GetMeshDS();
6838 TNodeNodeMap nodeNodeMap; // node to replace - new node
6839 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6840 list< int > rmElemIds, rmNodeIds;
6841 vector< ElemFeatures > newElemDefs;
6843 // Fill nodeNodeMap and elems
6845 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6846 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6848 list<const SMDS_MeshNode*>& nodes = *grIt;
6849 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6850 const SMDS_MeshNode* nToKeep = *nIt;
6851 for ( ++nIt; nIt != nodes.end(); nIt++ )
6853 const SMDS_MeshNode* nToRemove = *nIt;
6854 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6855 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6856 while ( invElemIt->more() ) {
6857 const SMDS_MeshElement* elem = invElemIt->next();
6863 // Apply recursive replacements (BUG 0020185)
6864 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6865 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6867 const SMDS_MeshNode* nToKeep = nnIt->second;
6868 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6869 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6871 nToKeep = nnIt_i->second;
6872 nnIt->second = nToKeep;
6873 nnIt_i = nodeNodeMap.find( nToKeep );
6877 if ( theAvoidMakingHoles )
6879 // find elements whose topology changes
6881 vector<const SMDS_MeshElement*> pbElems;
6882 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6883 for ( ; eIt != elems.end(); ++eIt )
6885 const SMDS_MeshElement* elem = *eIt;
6886 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6887 while ( itN->more() )
6889 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6890 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6891 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6893 // several nodes of elem stick
6894 pbElems.push_back( elem );
6899 // exclude from merge nodes causing spoiling element
6900 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6902 bool nodesExcluded = false;
6903 for ( size_t i = 0; i < pbElems.size(); ++i )
6905 size_t prevNbMergeNodes = nodeNodeMap.size();
6906 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6907 prevNbMergeNodes < nodeNodeMap.size() )
6908 nodesExcluded = true;
6910 if ( !nodesExcluded )
6915 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6917 const SMDS_MeshNode* nToRemove = nnIt->first;
6918 const SMDS_MeshNode* nToKeep = nnIt->second;
6919 if ( nToRemove != nToKeep )
6921 rmNodeIds.push_back( nToRemove->GetID() );
6922 AddToSameGroups( nToKeep, nToRemove, mesh );
6923 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6924 // w/o creating node in place of merged ones.
6925 SMDS_PositionPtr pos = nToRemove->GetPosition();
6926 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6927 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6928 sm->SetIsAlwaysComputed( true );
6932 // Change element nodes or remove an element
6934 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6935 for ( ; eIt != elems.end(); eIt++ )
6937 const SMDS_MeshElement* elem = *eIt;
6938 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6940 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6942 rmElemIds.push_back( elem->GetID() );
6944 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6946 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6947 & newElemDefs[i].myNodes[0],
6948 newElemDefs[i].myNodes.size() ))
6952 newElemDefs[i].SetID( elem->GetID() );
6953 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6954 if ( !keepElem ) rmElemIds.pop_back();
6958 newElemDefs[i].SetID( -1 );
6960 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6961 if ( sm && newElem )
6962 sm->AddElement( newElem );
6963 if ( elem != newElem )
6964 ReplaceElemInGroups( elem, newElem, mesh );
6969 // Remove bad elements, then equal nodes (order important)
6970 Remove( rmElemIds, /*isNodes=*/false );
6971 Remove( rmNodeIds, /*isNodes=*/true );
6976 //=======================================================================
6977 //function : applyMerge
6978 //purpose : Compute new connectivity of an element after merging nodes
6979 // \param [in] elems - the element
6980 // \param [out] newElemDefs - definition(s) of result element(s)
6981 // \param [inout] nodeNodeMap - nodes to merge
6982 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6983 // after merging (but not degenerated), removes nodes causing
6984 // the invalidity from \a nodeNodeMap.
6985 // \return bool - true if the element should be removed
6986 //=======================================================================
6988 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6989 vector< ElemFeatures >& newElemDefs,
6990 TNodeNodeMap& nodeNodeMap,
6991 const bool avoidMakingHoles )
6993 bool toRemove = false; // to remove elem
6994 int nbResElems = 1; // nb new elements
6996 newElemDefs.resize(nbResElems);
6997 newElemDefs[0].Init( elem );
6998 newElemDefs[0].myNodes.clear();
7000 set<const SMDS_MeshNode*> nodeSet;
7001 vector< const SMDS_MeshNode*> curNodes;
7002 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7005 const int nbNodes = elem->NbNodes();
7006 SMDSAbs_EntityType entity = elem->GetEntityType();
7008 curNodes.resize( nbNodes );
7009 uniqueNodes.resize( nbNodes );
7010 iRepl.resize( nbNodes );
7011 int iUnique = 0, iCur = 0, nbRepl = 0;
7013 // Get new seq of nodes
7015 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7016 while ( itN->more() )
7018 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7020 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7021 if ( nnIt != nodeNodeMap.end() ) {
7024 curNodes[ iCur ] = n;
7025 bool isUnique = nodeSet.insert( n ).second;
7027 uniqueNodes[ iUnique++ ] = n;
7029 iRepl[ nbRepl++ ] = iCur;
7033 // Analyse element topology after replacement
7035 int nbUniqueNodes = nodeSet.size();
7036 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7041 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7043 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7044 int nbCorners = nbNodes / 2;
7045 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7047 int iNext = ( iCur + 1 ) % nbCorners;
7048 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7050 int iMedium = iCur + nbCorners;
7051 vector< const SMDS_MeshNode* >::iterator i =
7052 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7054 curNodes[ iMedium ]);
7055 if ( i != uniqueNodes.end() )
7058 for ( ; i+1 != uniqueNodes.end(); ++i )
7067 case SMDSEntity_Polygon:
7068 case SMDSEntity_Quad_Polygon: // Polygon
7070 ElemFeatures* elemType = & newElemDefs[0];
7071 const bool isQuad = elemType->myIsQuad;
7073 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7074 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7076 // a polygon can divide into several elements
7077 vector<const SMDS_MeshNode *> polygons_nodes;
7078 vector<int> quantities;
7079 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7080 newElemDefs.resize( nbResElems );
7081 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7083 ElemFeatures* elemType = & newElemDefs[iface];
7084 if ( iface ) elemType->Init( elem );
7086 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7087 int nbNewNodes = quantities[iface];
7088 face_nodes.assign( polygons_nodes.begin() + inode,
7089 polygons_nodes.begin() + inode + nbNewNodes );
7090 inode += nbNewNodes;
7091 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7093 bool isValid = ( nbNewNodes % 2 == 0 );
7094 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7095 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7096 elemType->SetQuad( isValid );
7097 if ( isValid ) // put medium nodes after corners
7098 SMDS_MeshCell::applyInterlaceRev
7099 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7100 nbNewNodes ), face_nodes );
7102 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7104 nbUniqueNodes = newElemDefs[0].myNodes.size();
7108 case SMDSEntity_Polyhedra: // Polyhedral volume
7110 if ( nbUniqueNodes >= 4 )
7112 // each face has to be analyzed in order to check volume validity
7113 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7115 int nbFaces = aPolyedre->NbFaces();
7117 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7118 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7119 vector<const SMDS_MeshNode *> faceNodes;
7123 for (int iface = 1; iface <= nbFaces; iface++)
7125 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7126 faceNodes.resize( nbFaceNodes );
7127 for (int inode = 1; inode <= nbFaceNodes; inode++)
7129 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7130 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7131 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7132 faceNode = (*nnIt).second;
7133 faceNodes[inode - 1] = faceNode;
7135 SimplifyFace(faceNodes, poly_nodes, quantities);
7138 if ( quantities.size() > 3 )
7140 // TODO: remove coincident faces
7142 nbUniqueNodes = newElemDefs[0].myNodes.size();
7150 // TODO not all the possible cases are solved. Find something more generic?
7151 case SMDSEntity_Edge: //////// EDGE
7152 case SMDSEntity_Triangle: //// TRIANGLE
7153 case SMDSEntity_Quad_Triangle:
7154 case SMDSEntity_Tetra:
7155 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7159 case SMDSEntity_Quad_Edge:
7163 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7165 if ( nbUniqueNodes < 3 )
7167 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7168 toRemove = true; // opposite nodes stick
7173 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7182 if ( nbUniqueNodes == 6 &&
7184 ( nbRepl == 1 || iRepl[1] >= 4 ))
7190 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7199 if ( nbUniqueNodes == 7 &&
7201 ( nbRepl == 1 || iRepl[1] != 8 ))
7207 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7209 if ( nbUniqueNodes == 4 ) {
7210 // ---------------------------------> tetrahedron
7211 if ( curNodes[3] == curNodes[4] &&
7212 curNodes[3] == curNodes[5] ) {
7216 else if ( curNodes[0] == curNodes[1] &&
7217 curNodes[0] == curNodes[2] ) {
7218 // bottom nodes stick: set a top before
7219 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7220 uniqueNodes[ 0 ] = curNodes [ 5 ];
7221 uniqueNodes[ 1 ] = curNodes [ 4 ];
7222 uniqueNodes[ 2 ] = curNodes [ 3 ];
7225 else if (( curNodes[0] == curNodes[3] ) +
7226 ( curNodes[1] == curNodes[4] ) +
7227 ( curNodes[2] == curNodes[5] ) == 2 ) {
7228 // a lateral face turns into a line
7232 else if ( nbUniqueNodes == 5 ) {
7233 // PENTAHEDRON --------------------> pyramid
7234 if ( curNodes[0] == curNodes[3] )
7236 uniqueNodes[ 0 ] = curNodes[ 1 ];
7237 uniqueNodes[ 1 ] = curNodes[ 4 ];
7238 uniqueNodes[ 2 ] = curNodes[ 5 ];
7239 uniqueNodes[ 3 ] = curNodes[ 2 ];
7240 uniqueNodes[ 4 ] = curNodes[ 0 ];
7243 if ( curNodes[1] == curNodes[4] )
7245 uniqueNodes[ 0 ] = curNodes[ 0 ];
7246 uniqueNodes[ 1 ] = curNodes[ 2 ];
7247 uniqueNodes[ 2 ] = curNodes[ 5 ];
7248 uniqueNodes[ 3 ] = curNodes[ 3 ];
7249 uniqueNodes[ 4 ] = curNodes[ 1 ];
7252 if ( curNodes[2] == curNodes[5] )
7254 uniqueNodes[ 0 ] = curNodes[ 0 ];
7255 uniqueNodes[ 1 ] = curNodes[ 3 ];
7256 uniqueNodes[ 2 ] = curNodes[ 4 ];
7257 uniqueNodes[ 3 ] = curNodes[ 1 ];
7258 uniqueNodes[ 4 ] = curNodes[ 2 ];
7264 case SMDSEntity_Hexa:
7266 //////////////////////////////////// HEXAHEDRON
7267 SMDS_VolumeTool hexa (elem);
7268 hexa.SetExternalNormal();
7269 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7270 //////////////////////// HEX ---> tetrahedron
7271 for ( int iFace = 0; iFace < 6; iFace++ ) {
7272 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7273 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7274 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7275 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7276 // one face turns into a point ...
7277 int pickInd = ind[ 0 ];
7278 int iOppFace = hexa.GetOppFaceIndex( iFace );
7279 ind = hexa.GetFaceNodesIndices( iOppFace );
7281 uniqueNodes.clear();
7282 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7283 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7286 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7288 if ( nbStick == 1 ) {
7289 // ... and the opposite one - into a triangle.
7291 uniqueNodes.push_back( curNodes[ pickInd ]);
7298 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7299 //////////////////////// HEX ---> prism
7300 int nbTria = 0, iTria[3];
7301 const int *ind; // indices of face nodes
7302 // look for triangular faces
7303 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7304 ind = hexa.GetFaceNodesIndices( iFace );
7305 TIDSortedNodeSet faceNodes;
7306 for ( iCur = 0; iCur < 4; iCur++ )
7307 faceNodes.insert( curNodes[ind[iCur]] );
7308 if ( faceNodes.size() == 3 )
7309 iTria[ nbTria++ ] = iFace;
7311 // check if triangles are opposite
7312 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7314 // set nodes of the bottom triangle
7315 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7317 for ( iCur = 0; iCur < 4; iCur++ )
7318 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7319 indB.push_back( ind[iCur] );
7320 if ( !hexa.IsForward() )
7321 std::swap( indB[0], indB[2] );
7322 for ( iCur = 0; iCur < 3; iCur++ )
7323 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7324 // set nodes of the top triangle
7325 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7326 for ( iCur = 0; iCur < 3; ++iCur )
7327 for ( int j = 0; j < 4; ++j )
7328 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7330 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7337 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7338 //////////////////// HEXAHEDRON ---> pyramid
7339 for ( int iFace = 0; iFace < 6; iFace++ ) {
7340 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7341 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7342 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7343 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7344 // one face turns into a point ...
7345 int iOppFace = hexa.GetOppFaceIndex( iFace );
7346 ind = hexa.GetFaceNodesIndices( iOppFace );
7347 uniqueNodes.clear();
7348 for ( iCur = 0; iCur < 4; iCur++ ) {
7349 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7352 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7354 if ( uniqueNodes.size() == 4 ) {
7355 // ... and the opposite one is a quadrangle
7357 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7358 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7366 if ( toRemove && nbUniqueNodes > 4 ) {
7367 ////////////////// HEXAHEDRON ---> polyhedron
7368 hexa.SetExternalNormal();
7369 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7370 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7371 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7372 quantities.reserve( 6 ); quantities.clear();
7373 for ( int iFace = 0; iFace < 6; iFace++ )
7375 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7376 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7377 curNodes[ind[1]] == curNodes[ind[3]] )
7380 break; // opposite nodes stick
7383 for ( iCur = 0; iCur < 4; iCur++ )
7385 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7386 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7388 if ( nodeSet.size() < 3 )
7389 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7391 quantities.push_back( nodeSet.size() );
7393 if ( quantities.size() >= 4 )
7396 nbUniqueNodes = poly_nodes.size();
7397 newElemDefs[0].SetPoly(true);
7401 } // case HEXAHEDRON
7406 } // switch ( entity )
7408 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7410 // erase from nodeNodeMap nodes whose merge spoils elem
7411 vector< const SMDS_MeshNode* > noMergeNodes;
7412 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7413 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7414 nodeNodeMap.erase( noMergeNodes[i] );
7417 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7419 uniqueNodes.resize( nbUniqueNodes );
7421 if ( !toRemove && nbResElems == 0 )
7424 newElemDefs.resize( nbResElems );
7430 // ========================================================
7431 // class : ComparableElement
7432 // purpose : allow comparing elements basing on their nodes
7433 // ========================================================
7435 class ComparableElement : public boost::container::flat_set< int >
7437 typedef boost::container::flat_set< int > int_set;
7439 const SMDS_MeshElement* myElem;
7441 mutable int myGroupID;
7445 ComparableElement( const SMDS_MeshElement* theElem ):
7446 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7448 this->reserve( theElem->NbNodes() );
7449 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7451 int id = nodeIt->next()->GetID();
7457 const SMDS_MeshElement* GetElem() const { return myElem; }
7459 int& GroupID() const { return myGroupID; }
7460 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7462 ComparableElement( const ComparableElement& theSource ) // move copy
7464 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7465 (int_set&) (*this ) = boost::move( src );
7466 myElem = src.myElem;
7467 mySumID = src.mySumID;
7468 myGroupID = src.myGroupID;
7471 static int HashCode(const ComparableElement& se, int limit )
7473 return ::HashCode( se.mySumID, limit );
7475 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7477 return ( se1 == se2 );
7482 //=======================================================================
7483 //function : FindEqualElements
7484 //purpose : Return list of group of elements built on the same nodes.
7485 // Search among theElements or in the whole mesh if theElements is empty
7486 //=======================================================================
7488 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7489 TListOfListOfElementsID & theGroupsOfElementsID )
7493 SMDS_ElemIteratorPtr elemIt;
7494 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7495 else elemIt = SMESHUtils::elemSetIterator( theElements );
7497 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7498 typedef std::list<int> TGroupOfElems;
7499 TMapOfElements mapOfElements;
7500 std::vector< TGroupOfElems > arrayOfGroups;
7501 TGroupOfElems groupOfElems;
7503 while ( elemIt->more() )
7505 const SMDS_MeshElement* curElem = elemIt->next();
7506 if ( curElem->IsNull() )
7508 ComparableElement compElem = curElem;
7510 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7511 if ( elemInSet.GetElem() != curElem ) // coincident elem
7513 int& iG = elemInSet.GroupID();
7516 iG = arrayOfGroups.size();
7517 arrayOfGroups.push_back( groupOfElems );
7518 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7520 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7524 groupOfElems.clear();
7525 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7526 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7528 if ( groupIt->size() > 1 ) {
7529 //groupOfElems.sort(); -- theElements are sorted already
7530 theGroupsOfElementsID.emplace_back( *groupIt );
7535 //=======================================================================
7536 //function : MergeElements
7537 //purpose : In each given group, substitute all elements by the first one.
7538 //=======================================================================
7540 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7544 typedef list<int> TListOfIDs;
7545 TListOfIDs rmElemIds; // IDs of elems to remove
7547 SMESHDS_Mesh* aMesh = GetMeshDS();
7549 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7550 while ( groupsIt != theGroupsOfElementsID.end() ) {
7551 TListOfIDs& aGroupOfElemID = *groupsIt;
7552 aGroupOfElemID.sort();
7553 int elemIDToKeep = aGroupOfElemID.front();
7554 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7555 aGroupOfElemID.pop_front();
7556 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7557 while ( idIt != aGroupOfElemID.end() ) {
7558 int elemIDToRemove = *idIt;
7559 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7560 // add the kept element in groups of removed one (PAL15188)
7561 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7562 rmElemIds.push_back( elemIDToRemove );
7568 Remove( rmElemIds, false );
7571 //=======================================================================
7572 //function : MergeEqualElements
7573 //purpose : Remove all but one of elements built on the same nodes.
7574 //=======================================================================
7576 void SMESH_MeshEditor::MergeEqualElements()
7578 TIDSortedElemSet aMeshElements; /* empty input ==
7579 to merge equal elements in the whole mesh */
7580 TListOfListOfElementsID aGroupsOfElementsID;
7581 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7582 MergeElements( aGroupsOfElementsID );
7585 //=======================================================================
7586 //function : findAdjacentFace
7588 //=======================================================================
7590 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7591 const SMDS_MeshNode* n2,
7592 const SMDS_MeshElement* elem)
7594 TIDSortedElemSet elemSet, avoidSet;
7596 avoidSet.insert ( elem );
7597 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7600 //=======================================================================
7601 //function : findSegment
7602 //purpose : Return a mesh segment by two nodes one of which can be medium
7603 //=======================================================================
7605 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7606 const SMDS_MeshNode* n2)
7608 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7609 while ( it->more() )
7611 const SMDS_MeshElement* seg = it->next();
7612 if ( seg->GetNodeIndex( n2 ) >= 0 )
7618 //=======================================================================
7619 //function : FindFreeBorder
7621 //=======================================================================
7623 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7625 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7626 const SMDS_MeshNode* theSecondNode,
7627 const SMDS_MeshNode* theLastNode,
7628 list< const SMDS_MeshNode* > & theNodes,
7629 list< const SMDS_MeshElement* >& theFaces)
7631 if ( !theFirstNode || !theSecondNode )
7633 // find border face between theFirstNode and theSecondNode
7634 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7638 theFaces.push_back( curElem );
7639 theNodes.push_back( theFirstNode );
7640 theNodes.push_back( theSecondNode );
7642 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7643 //TIDSortedElemSet foundElems;
7644 bool needTheLast = ( theLastNode != 0 );
7646 vector<const SMDS_MeshNode*> nodes;
7648 while ( nStart != theLastNode ) {
7649 if ( nStart == theFirstNode )
7650 return !needTheLast;
7652 // find all free border faces sharing nStart
7654 list< const SMDS_MeshElement* > curElemList;
7655 list< const SMDS_MeshNode* > nStartList;
7656 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7657 while ( invElemIt->more() ) {
7658 const SMDS_MeshElement* e = invElemIt->next();
7659 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7662 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7663 SMDS_MeshElement::iterator() );
7664 nodes.push_back( nodes[ 0 ]);
7667 int iNode = 0, nbNodes = nodes.size() - 1;
7668 for ( iNode = 0; iNode < nbNodes; iNode++ )
7669 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7670 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7671 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7673 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7674 curElemList.push_back( e );
7678 // analyse the found
7680 int nbNewBorders = curElemList.size();
7681 if ( nbNewBorders == 0 ) {
7682 // no free border furthermore
7683 return !needTheLast;
7685 else if ( nbNewBorders == 1 ) {
7686 // one more element found
7688 nStart = nStartList.front();
7689 curElem = curElemList.front();
7690 theFaces.push_back( curElem );
7691 theNodes.push_back( nStart );
7694 // several continuations found
7695 list< const SMDS_MeshElement* >::iterator curElemIt;
7696 list< const SMDS_MeshNode* >::iterator nStartIt;
7697 // check if one of them reached the last node
7698 if ( needTheLast ) {
7699 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7700 curElemIt!= curElemList.end();
7701 curElemIt++, nStartIt++ )
7702 if ( *nStartIt == theLastNode ) {
7703 theFaces.push_back( *curElemIt );
7704 theNodes.push_back( *nStartIt );
7708 // find the best free border by the continuations
7709 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7710 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7711 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7712 curElemIt!= curElemList.end();
7713 curElemIt++, nStartIt++ )
7715 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7716 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7717 // find one more free border
7718 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7722 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7723 // choice: clear a worse one
7724 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7725 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7726 contNodes[ iWorse ].clear();
7727 contFaces[ iWorse ].clear();
7730 if ( contNodes[0].empty() && contNodes[1].empty() )
7733 // push_back the best free border
7734 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7735 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7736 //theNodes.pop_back(); // remove nIgnore
7737 theNodes.pop_back(); // remove nStart
7738 //theFaces.pop_back(); // remove curElem
7739 theNodes.splice( theNodes.end(), *cNL );
7740 theFaces.splice( theFaces.end(), *cFL );
7743 } // several continuations found
7744 } // while ( nStart != theLastNode )
7749 //=======================================================================
7750 //function : CheckFreeBorderNodes
7751 //purpose : Return true if the tree nodes are on a free border
7752 //=======================================================================
7754 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7755 const SMDS_MeshNode* theNode2,
7756 const SMDS_MeshNode* theNode3)
7758 list< const SMDS_MeshNode* > nodes;
7759 list< const SMDS_MeshElement* > faces;
7760 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7763 //=======================================================================
7764 //function : SewFreeBorder
7766 //warning : for border-to-side sewing theSideSecondNode is considered as
7767 // the last side node and theSideThirdNode is not used
7768 //=======================================================================
7770 SMESH_MeshEditor::Sew_Error
7771 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7772 const SMDS_MeshNode* theBordSecondNode,
7773 const SMDS_MeshNode* theBordLastNode,
7774 const SMDS_MeshNode* theSideFirstNode,
7775 const SMDS_MeshNode* theSideSecondNode,
7776 const SMDS_MeshNode* theSideThirdNode,
7777 const bool theSideIsFreeBorder,
7778 const bool toCreatePolygons,
7779 const bool toCreatePolyedrs)
7783 Sew_Error aResult = SEW_OK;
7785 // ====================================
7786 // find side nodes and elements
7787 // ====================================
7789 list< const SMDS_MeshNode* > nSide[ 2 ];
7790 list< const SMDS_MeshElement* > eSide[ 2 ];
7791 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7792 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7796 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7797 nSide[0], eSide[0])) {
7798 MESSAGE(" Free Border 1 not found " );
7799 aResult = SEW_BORDER1_NOT_FOUND;
7801 if (theSideIsFreeBorder) {
7804 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7805 nSide[1], eSide[1])) {
7806 MESSAGE(" Free Border 2 not found " );
7807 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7810 if ( aResult != SEW_OK )
7813 if (!theSideIsFreeBorder) {
7817 // -------------------------------------------------------------------------
7819 // 1. If nodes to merge are not coincident, move nodes of the free border
7820 // from the coord sys defined by the direction from the first to last
7821 // nodes of the border to the correspondent sys of the side 2
7822 // 2. On the side 2, find the links most co-directed with the correspondent
7823 // links of the free border
7824 // -------------------------------------------------------------------------
7826 // 1. Since sewing may break if there are volumes to split on the side 2,
7827 // we won't move nodes but just compute new coordinates for them
7828 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7829 TNodeXYZMap nBordXYZ;
7830 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7831 list< const SMDS_MeshNode* >::iterator nBordIt;
7833 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7834 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7835 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7836 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7837 double tol2 = 1.e-8;
7838 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7839 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7840 // Need node movement.
7842 // find X and Z axes to create trsf
7843 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7845 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7847 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7850 gp_Ax3 toBordAx( Pb1, Zb, X );
7851 gp_Ax3 fromSideAx( Ps1, Zs, X );
7852 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7854 gp_Trsf toBordSys, fromSide2Sys;
7855 toBordSys.SetTransformation( toBordAx );
7856 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7857 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7860 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7861 const SMDS_MeshNode* n = *nBordIt;
7862 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7863 toBordSys.Transforms( xyz );
7864 fromSide2Sys.Transforms( xyz );
7865 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7869 // just insert nodes XYZ in the nBordXYZ map
7870 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7871 const SMDS_MeshNode* n = *nBordIt;
7872 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7876 // 2. On the side 2, find the links most co-directed with the correspondent
7877 // links of the free border
7879 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7880 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7881 sideNodes.push_back( theSideFirstNode );
7883 bool hasVolumes = false;
7884 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7885 set<long> foundSideLinkIDs, checkedLinkIDs;
7886 SMDS_VolumeTool volume;
7887 //const SMDS_MeshNode* faceNodes[ 4 ];
7889 const SMDS_MeshNode* sideNode;
7890 const SMDS_MeshElement* sideElem = 0;
7891 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7892 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7893 nBordIt = bordNodes.begin();
7895 // border node position and border link direction to compare with
7896 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7897 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7898 // choose next side node by link direction or by closeness to
7899 // the current border node:
7900 bool searchByDir = ( *nBordIt != theBordLastNode );
7902 // find the next node on the Side 2
7904 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7906 checkedLinkIDs.clear();
7907 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7909 // loop on inverse elements of current node (prevSideNode) on the Side 2
7910 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7911 while ( invElemIt->more() )
7913 const SMDS_MeshElement* elem = invElemIt->next();
7914 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7915 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7916 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7917 bool isVolume = volume.Set( elem );
7918 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7919 if ( isVolume ) // --volume
7921 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7922 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7923 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7924 while ( nIt->more() ) {
7925 nodes[ iNode ] = cast2Node( nIt->next() );
7926 if ( nodes[ iNode++ ] == prevSideNode )
7927 iPrevNode = iNode - 1;
7929 // there are 2 links to check
7934 // loop on links, to be precise, on the second node of links
7935 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7936 const SMDS_MeshNode* n = nodes[ iNode ];
7938 if ( !volume.IsLinked( n, prevSideNode ))
7942 if ( iNode ) // a node before prevSideNode
7943 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7944 else // a node after prevSideNode
7945 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7947 // check if this link was already used
7948 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7949 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7950 if (!isJustChecked &&
7951 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7953 // test a link geometrically
7954 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7955 bool linkIsBetter = false;
7956 double dot = 0.0, dist = 0.0;
7957 if ( searchByDir ) { // choose most co-directed link
7958 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7959 linkIsBetter = ( dot > maxDot );
7961 else { // choose link with the node closest to bordPos
7962 dist = ( nextXYZ - bordPos ).SquareModulus();
7963 linkIsBetter = ( dist < minDist );
7965 if ( linkIsBetter ) {
7974 } // loop on inverse elements of prevSideNode
7977 MESSAGE(" Can't find path by links of the Side 2 ");
7978 return SEW_BAD_SIDE_NODES;
7980 sideNodes.push_back( sideNode );
7981 sideElems.push_back( sideElem );
7982 foundSideLinkIDs.insert ( linkID );
7983 prevSideNode = sideNode;
7985 if ( *nBordIt == theBordLastNode )
7986 searchByDir = false;
7988 // find the next border link to compare with
7989 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7990 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7991 // move to next border node if sideNode is before forward border node (bordPos)
7992 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7993 prevBordNode = *nBordIt;
7995 bordPos = nBordXYZ[ *nBordIt ];
7996 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7997 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8001 while ( sideNode != theSideSecondNode );
8003 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8004 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8005 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8007 } // end nodes search on the side 2
8009 // ============================
8010 // sew the border to the side 2
8011 // ============================
8013 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8014 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8016 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8017 if ( toMergeConformal && toCreatePolygons )
8019 // do not merge quadrangles if polygons are OK (IPAL0052824)
8020 eIt[0] = eSide[0].begin();
8021 eIt[1] = eSide[1].begin();
8022 bool allQuads[2] = { true, true };
8023 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8024 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8025 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8027 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8030 TListOfListOfNodes nodeGroupsToMerge;
8031 if (( toMergeConformal ) ||
8032 ( theSideIsFreeBorder && !theSideThirdNode )) {
8034 // all nodes are to be merged
8036 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8037 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8038 nIt[0]++, nIt[1]++ )
8040 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8041 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8042 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8047 // insert new nodes into the border and the side to get equal nb of segments
8049 // get normalized parameters of nodes on the borders
8050 vector< double > param[ 2 ];
8051 param[0].resize( maxNbNodes );
8052 param[1].resize( maxNbNodes );
8054 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8055 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8056 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8057 const SMDS_MeshNode* nPrev = *nIt;
8058 double bordLength = 0;
8059 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8060 const SMDS_MeshNode* nCur = *nIt;
8061 gp_XYZ segment (nCur->X() - nPrev->X(),
8062 nCur->Y() - nPrev->Y(),
8063 nCur->Z() - nPrev->Z());
8064 double segmentLen = segment.Modulus();
8065 bordLength += segmentLen;
8066 param[ iBord ][ iNode ] = bordLength;
8069 // normalize within [0,1]
8070 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8071 param[ iBord ][ iNode ] /= bordLength;
8075 // loop on border segments
8076 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8077 int i[ 2 ] = { 0, 0 };
8078 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8079 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8081 // element can be split while iterating on border if it has two edges in the border
8082 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8083 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8085 TElemOfNodeListMap insertMap;
8086 TElemOfNodeListMap::iterator insertMapIt;
8088 // key: elem to insert nodes into
8089 // value: 2 nodes to insert between + nodes to be inserted
8091 bool next[ 2 ] = { false, false };
8093 // find min adjacent segment length after sewing
8094 double nextParam = 10., prevParam = 0;
8095 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8096 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8097 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8098 if ( i[ iBord ] > 0 )
8099 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8101 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8102 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8103 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8105 // choose to insert or to merge nodes
8106 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8107 if ( Abs( du ) <= minSegLen * 0.2 ) {
8110 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8111 const SMDS_MeshNode* n0 = *nIt[0];
8112 const SMDS_MeshNode* n1 = *nIt[1];
8113 nodeGroupsToMerge.back().push_back( n1 );
8114 nodeGroupsToMerge.back().push_back( n0 );
8115 // position of node of the border changes due to merge
8116 param[ 0 ][ i[0] ] += du;
8117 // move n1 for the sake of elem shape evaluation during insertion.
8118 // n1 will be removed by MergeNodes() anyway
8119 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8120 next[0] = next[1] = true;
8125 int intoBord = ( du < 0 ) ? 0 : 1;
8126 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8127 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8128 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8129 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8130 if ( intoBord == 1 ) {
8131 // move node of the border to be on a link of elem of the side
8132 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8133 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8134 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8135 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8137 elemReplaceMapIt = elemReplaceMap.find( elem );
8138 if ( elemReplaceMapIt != elemReplaceMap.end() )
8139 elem = elemReplaceMapIt->second;
8141 insertMapIt = insertMap.find( elem );
8142 bool notFound = ( insertMapIt == insertMap.end() );
8143 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8145 // insert into another link of the same element:
8146 // 1. perform insertion into the other link of the elem
8147 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8148 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8149 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8150 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8151 // 2. perform insertion into the link of adjacent faces
8152 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8153 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8155 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8156 InsertNodesIntoLink( seg, n12, n22, nodeList );
8158 if (toCreatePolyedrs) {
8159 // perform insertion into the links of adjacent volumes
8160 UpdateVolumes(n12, n22, nodeList);
8162 // 3. find an element appeared on n1 and n2 after the insertion
8163 insertMap.erase( insertMapIt );
8164 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8165 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8168 if ( notFound || otherLink ) {
8169 // add element and nodes of the side into the insertMap
8170 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8171 (*insertMapIt).second.push_back( n1 );
8172 (*insertMapIt).second.push_back( n2 );
8174 // add node to be inserted into elem
8175 (*insertMapIt).second.push_back( nIns );
8176 next[ 1 - intoBord ] = true;
8179 // go to the next segment
8180 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8181 if ( next[ iBord ] ) {
8182 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8184 nPrev[ iBord ] = *nIt[ iBord ];
8185 nIt[ iBord ]++; i[ iBord ]++;
8189 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8191 // perform insertion of nodes into elements
8193 for (insertMapIt = insertMap.begin();
8194 insertMapIt != insertMap.end();
8197 const SMDS_MeshElement* elem = (*insertMapIt).first;
8198 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8199 if ( nodeList.size() < 3 ) continue;
8200 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8201 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8203 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8205 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8206 InsertNodesIntoLink( seg, n1, n2, nodeList );
8209 if ( !theSideIsFreeBorder ) {
8210 // look for and insert nodes into the faces adjacent to elem
8211 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8212 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8215 if (toCreatePolyedrs) {
8216 // perform insertion into the links of adjacent volumes
8217 UpdateVolumes(n1, n2, nodeList);
8220 } // end: insert new nodes
8222 MergeNodes ( nodeGroupsToMerge );
8225 // Remove coincident segments
8228 TIDSortedElemSet segments;
8229 SMESH_SequenceOfElemPtr newFaces;
8230 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8232 if ( !myLastCreatedElems[i] ) continue;
8233 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8234 segments.insert( segments.end(), myLastCreatedElems[i] );
8236 newFaces.push_back( myLastCreatedElems[i] );
8238 // get segments adjacent to merged nodes
8239 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8240 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8242 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8243 if ( nodes.front()->IsNull() ) continue;
8244 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8245 while ( segIt->more() )
8246 segments.insert( segIt->next() );
8250 TListOfListOfElementsID equalGroups;
8251 if ( !segments.empty() )
8252 FindEqualElements( segments, equalGroups );
8253 if ( !equalGroups.empty() )
8255 // remove from segments those that will be removed
8256 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8257 for ( ; itGroups != equalGroups.end(); ++itGroups )
8259 list< int >& group = *itGroups;
8260 list< int >::iterator id = group.begin();
8261 for ( ++id; id != group.end(); ++id )
8262 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8263 segments.erase( seg );
8265 // remove equal segments
8266 MergeElements( equalGroups );
8268 // restore myLastCreatedElems
8269 myLastCreatedElems = newFaces;
8270 TIDSortedElemSet::iterator seg = segments.begin();
8271 for ( ; seg != segments.end(); ++seg )
8272 myLastCreatedElems.push_back( *seg );
8278 //=======================================================================
8279 //function : InsertNodesIntoLink
8280 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8281 // and theBetweenNode2 and split theElement
8282 //=======================================================================
8284 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8285 const SMDS_MeshNode* theBetweenNode1,
8286 const SMDS_MeshNode* theBetweenNode2,
8287 list<const SMDS_MeshNode*>& theNodesToInsert,
8288 const bool toCreatePoly)
8290 if ( !theElement ) return;
8292 SMESHDS_Mesh *aMesh = GetMeshDS();
8293 vector<const SMDS_MeshElement*> newElems;
8295 if ( theElement->GetType() == SMDSAbs_Edge )
8297 theNodesToInsert.push_front( theBetweenNode1 );
8298 theNodesToInsert.push_back ( theBetweenNode2 );
8299 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8300 const SMDS_MeshNode* n1 = *n;
8301 for ( ++n; n != theNodesToInsert.end(); ++n )
8303 const SMDS_MeshNode* n2 = *n;
8304 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8305 AddToSameGroups( seg, theElement, aMesh );
8307 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8310 theNodesToInsert.pop_front();
8311 theNodesToInsert.pop_back();
8313 if ( theElement->IsQuadratic() ) // add a not split part
8315 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8316 theElement->end_nodes() );
8317 int iOther = 0, nbN = nodes.size();
8318 for ( ; iOther < nbN; ++iOther )
8319 if ( nodes[iOther] != theBetweenNode1 &&
8320 nodes[iOther] != theBetweenNode2 )
8324 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8325 AddToSameGroups( seg, theElement, aMesh );
8327 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8329 else if ( iOther == 2 )
8331 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8332 AddToSameGroups( seg, theElement, aMesh );
8334 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8337 // treat new elements
8338 for ( size_t i = 0; i < newElems.size(); ++i )
8341 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8342 myLastCreatedElems.push_back( newElems[i] );
8344 ReplaceElemInGroups( theElement, newElems, aMesh );
8345 aMesh->RemoveElement( theElement );
8348 } // if ( theElement->GetType() == SMDSAbs_Edge )
8350 const SMDS_MeshElement* theFace = theElement;
8351 if ( theFace->GetType() != SMDSAbs_Face ) return;
8353 // find indices of 2 link nodes and of the rest nodes
8354 int iNode = 0, il1, il2, i3, i4;
8355 il1 = il2 = i3 = i4 = -1;
8356 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8358 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8359 while ( nodeIt->more() ) {
8360 const SMDS_MeshNode* n = nodeIt->next();
8361 if ( n == theBetweenNode1 )
8363 else if ( n == theBetweenNode2 )
8369 nodes[ iNode++ ] = n;
8371 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8374 // arrange link nodes to go one after another regarding the face orientation
8375 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8376 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8381 aNodesToInsert.reverse();
8383 // check that not link nodes of a quadrangles are in good order
8384 int nbFaceNodes = theFace->NbNodes();
8385 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8391 if (toCreatePoly || theFace->IsPoly()) {
8394 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8396 // add nodes of face up to first node of link
8398 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8399 while ( nodeIt->more() && !isFLN ) {
8400 const SMDS_MeshNode* n = nodeIt->next();
8401 poly_nodes[iNode++] = n;
8402 isFLN = ( n == nodes[il1] );
8404 // add nodes to insert
8405 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8406 for (; nIt != aNodesToInsert.end(); nIt++) {
8407 poly_nodes[iNode++] = *nIt;
8409 // add nodes of face starting from last node of link
8410 while ( nodeIt->more() ) {
8411 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8412 poly_nodes[iNode++] = n;
8416 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8419 else if ( !theFace->IsQuadratic() )
8421 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8422 int nbLinkNodes = 2 + aNodesToInsert.size();
8423 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8424 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8425 linkNodes[ 0 ] = nodes[ il1 ];
8426 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8427 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8428 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8429 linkNodes[ iNode++ ] = *nIt;
8431 // decide how to split a quadrangle: compare possible variants
8432 // and choose which of splits to be a quadrangle
8433 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8434 if ( nbFaceNodes == 3 ) {
8435 iBestQuad = nbSplits;
8438 else if ( nbFaceNodes == 4 ) {
8439 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8440 double aBestRate = DBL_MAX;
8441 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8443 double aBadRate = 0;
8444 // evaluate elements quality
8445 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8446 if ( iSplit == iQuad ) {
8447 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8451 aBadRate += getBadRate( &quad, aCrit );
8454 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8456 nodes[ iSplit < iQuad ? i4 : i3 ]);
8457 aBadRate += getBadRate( &tria, aCrit );
8461 if ( aBadRate < aBestRate ) {
8463 aBestRate = aBadRate;
8468 // create new elements
8470 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8472 if ( iSplit == iBestQuad )
8473 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8478 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8480 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8483 const SMDS_MeshNode* newNodes[ 4 ];
8484 newNodes[ 0 ] = linkNodes[ i1 ];
8485 newNodes[ 1 ] = linkNodes[ i2 ];
8486 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8487 newNodes[ 3 ] = nodes[ i4 ];
8488 if (iSplit == iBestQuad)
8489 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8491 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8493 } // end if(!theFace->IsQuadratic())
8495 else { // theFace is quadratic
8496 // we have to split theFace on simple triangles and one simple quadrangle
8498 int nbshift = tmp*2;
8499 // shift nodes in nodes[] by nbshift
8501 for(i=0; i<nbshift; i++) {
8502 const SMDS_MeshNode* n = nodes[0];
8503 for(j=0; j<nbFaceNodes-1; j++) {
8504 nodes[j] = nodes[j+1];
8506 nodes[nbFaceNodes-1] = n;
8508 il1 = il1 - nbshift;
8509 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8510 // n0 n1 n2 n0 n1 n2
8511 // +-----+-----+ +-----+-----+
8520 // create new elements
8522 if ( nbFaceNodes == 6 ) { // quadratic triangle
8523 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8524 if ( theFace->IsMediumNode(nodes[il1]) ) {
8525 // create quadrangle
8526 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8532 // create quadrangle
8533 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8539 else { // nbFaceNodes==8 - quadratic quadrangle
8540 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8541 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8542 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8543 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8544 // create quadrangle
8545 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8551 // create quadrangle
8552 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8558 // create needed triangles using n1,n2,n3 and inserted nodes
8559 int nbn = 2 + aNodesToInsert.size();
8560 vector<const SMDS_MeshNode*> aNodes(nbn);
8561 aNodes[0 ] = nodes[n1];
8562 aNodes[nbn-1] = nodes[n2];
8563 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8564 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8565 aNodes[iNode++] = *nIt;
8567 for ( i = 1; i < nbn; i++ )
8568 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8571 // remove the old face
8572 for ( size_t i = 0; i < newElems.size(); ++i )
8575 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8576 myLastCreatedElems.push_back( newElems[i] );
8578 ReplaceElemInGroups( theFace, newElems, aMesh );
8579 aMesh->RemoveElement(theFace);
8581 } // InsertNodesIntoLink()
8583 //=======================================================================
8584 //function : UpdateVolumes
8586 //=======================================================================
8588 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8589 const SMDS_MeshNode* theBetweenNode2,
8590 list<const SMDS_MeshNode*>& theNodesToInsert)
8594 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8595 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8596 const SMDS_MeshElement* elem = invElemIt->next();
8598 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8599 SMDS_VolumeTool aVolume (elem);
8600 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8603 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8604 int iface, nbFaces = aVolume.NbFaces();
8605 vector<const SMDS_MeshNode *> poly_nodes;
8606 vector<int> quantities (nbFaces);
8608 for (iface = 0; iface < nbFaces; iface++) {
8609 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8610 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8611 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8613 for (int inode = 0; inode < nbFaceNodes; inode++) {
8614 poly_nodes.push_back(faceNodes[inode]);
8616 if (nbInserted == 0) {
8617 if (faceNodes[inode] == theBetweenNode1) {
8618 if (faceNodes[inode + 1] == theBetweenNode2) {
8619 nbInserted = theNodesToInsert.size();
8621 // add nodes to insert
8622 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8623 for (; nIt != theNodesToInsert.end(); nIt++) {
8624 poly_nodes.push_back(*nIt);
8628 else if (faceNodes[inode] == theBetweenNode2) {
8629 if (faceNodes[inode + 1] == theBetweenNode1) {
8630 nbInserted = theNodesToInsert.size();
8632 // add nodes to insert in reversed order
8633 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8635 for (; nIt != theNodesToInsert.begin(); nIt--) {
8636 poly_nodes.push_back(*nIt);
8638 poly_nodes.push_back(*nIt);
8645 quantities[iface] = nbFaceNodes + nbInserted;
8648 // Replace the volume
8649 SMESHDS_Mesh *aMesh = GetMeshDS();
8651 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8653 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8654 myLastCreatedElems.push_back( newElem );
8655 ReplaceElemInGroups( elem, newElem, aMesh );
8657 aMesh->RemoveElement( elem );
8663 //================================================================================
8665 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8667 //================================================================================
8669 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8670 vector<const SMDS_MeshNode *> & nodes,
8671 vector<int> & nbNodeInFaces )
8674 nbNodeInFaces.clear();
8675 SMDS_VolumeTool vTool ( elem );
8676 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8678 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8679 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8680 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8685 //=======================================================================
8687 * \brief Convert elements contained in a sub-mesh to quadratic
8688 * \return int - nb of checked elements
8690 //=======================================================================
8692 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8693 SMESH_MesherHelper& theHelper,
8694 const bool theForce3d)
8696 //MESSAGE("convertElemToQuadratic");
8698 if( !theSm ) return nbElem;
8700 vector<int> nbNodeInFaces;
8701 vector<const SMDS_MeshNode *> nodes;
8702 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8703 while(ElemItr->more())
8706 const SMDS_MeshElement* elem = ElemItr->next();
8707 if( !elem ) continue;
8709 // analyse a necessity of conversion
8710 const SMDSAbs_ElementType aType = elem->GetType();
8711 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8713 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8714 bool hasCentralNodes = false;
8715 if ( elem->IsQuadratic() )
8718 switch ( aGeomType ) {
8719 case SMDSEntity_Quad_Triangle:
8720 case SMDSEntity_Quad_Quadrangle:
8721 case SMDSEntity_Quad_Hexa:
8722 case SMDSEntity_Quad_Penta:
8723 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8725 case SMDSEntity_BiQuad_Triangle:
8726 case SMDSEntity_BiQuad_Quadrangle:
8727 case SMDSEntity_TriQuad_Hexa:
8728 case SMDSEntity_BiQuad_Penta:
8729 alreadyOK = theHelper.GetIsBiQuadratic();
8730 hasCentralNodes = true;
8735 // take into account already present medium nodes
8737 case SMDSAbs_Volume:
8738 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8740 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8742 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8748 // get elem data needed to re-create it
8750 const int id = elem->GetID();
8751 const int nbNodes = elem->NbCornerNodes();
8752 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8753 if ( aGeomType == SMDSEntity_Polyhedra )
8754 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8755 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8756 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8758 // remove a linear element
8759 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8761 // remove central nodes of biquadratic elements (biquad->quad conversion)
8762 if ( hasCentralNodes )
8763 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8764 if ( nodes[i]->NbInverseElements() == 0 )
8765 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8767 const SMDS_MeshElement* NewElem = 0;
8773 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8781 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8784 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8787 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8791 case SMDSAbs_Volume :
8795 case SMDSEntity_Tetra:
8796 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8798 case SMDSEntity_Pyramid:
8799 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8801 case SMDSEntity_Penta:
8802 case SMDSEntity_Quad_Penta:
8803 case SMDSEntity_BiQuad_Penta:
8804 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8806 case SMDSEntity_Hexa:
8807 case SMDSEntity_Quad_Hexa:
8808 case SMDSEntity_TriQuad_Hexa:
8809 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8810 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8812 case SMDSEntity_Hexagonal_Prism:
8814 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8821 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8822 if( NewElem && NewElem->getshapeId() < 1 )
8823 theSm->AddElement( NewElem );
8827 //=======================================================================
8828 //function : ConvertToQuadratic
8830 //=======================================================================
8832 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8834 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8835 SMESHDS_Mesh* meshDS = GetMeshDS();
8837 SMESH_MesherHelper aHelper(*myMesh);
8839 aHelper.SetIsQuadratic( true );
8840 aHelper.SetIsBiQuadratic( theToBiQuad );
8841 aHelper.SetElementsOnShape(true);
8842 aHelper.ToFixNodeParameters( true );
8844 // convert elements assigned to sub-meshes
8845 int nbCheckedElems = 0;
8846 if ( myMesh->HasShapeToMesh() )
8848 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8850 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8851 while ( smIt->more() ) {
8852 SMESH_subMesh* sm = smIt->next();
8853 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8854 aHelper.SetSubShape( sm->GetSubShape() );
8855 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8861 // convert elements NOT assigned to sub-meshes
8862 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8863 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8865 aHelper.SetElementsOnShape(false);
8866 SMESHDS_SubMesh *smDS = 0;
8869 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8870 while( aEdgeItr->more() )
8872 const SMDS_MeshEdge* edge = aEdgeItr->next();
8873 if ( !edge->IsQuadratic() )
8875 int id = edge->GetID();
8876 const SMDS_MeshNode* n1 = edge->GetNode(0);
8877 const SMDS_MeshNode* n2 = edge->GetNode(1);
8879 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8881 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8882 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8886 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8891 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8892 while( aFaceItr->more() )
8894 const SMDS_MeshFace* face = aFaceItr->next();
8895 if ( !face ) continue;
8897 const SMDSAbs_EntityType type = face->GetEntityType();
8901 case SMDSEntity_Quad_Triangle:
8902 case SMDSEntity_Quad_Quadrangle:
8903 alreadyOK = !theToBiQuad;
8904 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8906 case SMDSEntity_BiQuad_Triangle:
8907 case SMDSEntity_BiQuad_Quadrangle:
8908 alreadyOK = theToBiQuad;
8909 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8911 default: alreadyOK = false;
8916 const int id = face->GetID();
8917 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8919 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8921 SMDS_MeshFace * NewFace = 0;
8924 case SMDSEntity_Triangle:
8925 case SMDSEntity_Quad_Triangle:
8926 case SMDSEntity_BiQuad_Triangle:
8927 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8928 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8929 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8932 case SMDSEntity_Quadrangle:
8933 case SMDSEntity_Quad_Quadrangle:
8934 case SMDSEntity_BiQuad_Quadrangle:
8935 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8936 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8937 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8941 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8943 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8947 vector<int> nbNodeInFaces;
8948 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8949 while(aVolumeItr->more())
8951 const SMDS_MeshVolume* volume = aVolumeItr->next();
8952 if ( !volume ) continue;
8954 const SMDSAbs_EntityType type = volume->GetEntityType();
8955 if ( volume->IsQuadratic() )
8960 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8961 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8962 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8963 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8964 default: alreadyOK = true;
8968 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8972 const int id = volume->GetID();
8973 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8974 if ( type == SMDSEntity_Polyhedra )
8975 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8976 else if ( type == SMDSEntity_Hexagonal_Prism )
8977 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8979 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8981 SMDS_MeshVolume * NewVolume = 0;
8984 case SMDSEntity_Tetra:
8985 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8987 case SMDSEntity_Hexa:
8988 case SMDSEntity_Quad_Hexa:
8989 case SMDSEntity_TriQuad_Hexa:
8990 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8991 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8992 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8993 if ( nodes[i]->NbInverseElements() == 0 )
8994 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8996 case SMDSEntity_Pyramid:
8997 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8998 nodes[3], nodes[4], id, theForce3d);
9000 case SMDSEntity_Penta:
9001 case SMDSEntity_Quad_Penta:
9002 case SMDSEntity_BiQuad_Penta:
9003 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9004 nodes[3], nodes[4], nodes[5], id, theForce3d);
9005 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9006 if ( nodes[i]->NbInverseElements() == 0 )
9007 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9009 case SMDSEntity_Hexagonal_Prism:
9011 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9013 ReplaceElemInGroups(volume, NewVolume, meshDS);
9018 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9019 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9020 // aHelper.FixQuadraticElements(myError);
9021 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9025 //================================================================================
9027 * \brief Makes given elements quadratic
9028 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9029 * \param theElements - elements to make quadratic
9031 //================================================================================
9033 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9034 TIDSortedElemSet& theElements,
9035 const bool theToBiQuad)
9037 if ( theElements.empty() ) return;
9039 // we believe that all theElements are of the same type
9040 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9042 // get all nodes shared by theElements
9043 TIDSortedNodeSet allNodes;
9044 TIDSortedElemSet::iterator eIt = theElements.begin();
9045 for ( ; eIt != theElements.end(); ++eIt )
9046 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9048 // complete theElements with elements of lower dim whose all nodes are in allNodes
9050 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9051 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9052 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9053 for ( ; nIt != allNodes.end(); ++nIt )
9055 const SMDS_MeshNode* n = *nIt;
9056 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9057 while ( invIt->more() )
9059 const SMDS_MeshElement* e = invIt->next();
9060 const SMDSAbs_ElementType type = e->GetType();
9061 if ( e->IsQuadratic() )
9063 quadAdjacentElems[ type ].insert( e );
9066 switch ( e->GetEntityType() ) {
9067 case SMDSEntity_Quad_Triangle:
9068 case SMDSEntity_Quad_Quadrangle:
9069 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9070 case SMDSEntity_BiQuad_Triangle:
9071 case SMDSEntity_BiQuad_Quadrangle:
9072 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9073 default: alreadyOK = true;
9078 if ( type >= elemType )
9079 continue; // same type or more complex linear element
9081 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9082 continue; // e is already checked
9086 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9087 while ( nodeIt->more() && allIn )
9088 allIn = allNodes.count( nodeIt->next() );
9090 theElements.insert(e );
9094 SMESH_MesherHelper helper(*myMesh);
9095 helper.SetIsQuadratic( true );
9096 helper.SetIsBiQuadratic( theToBiQuad );
9098 // add links of quadratic adjacent elements to the helper
9100 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9101 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9102 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9104 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9106 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9107 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9108 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9110 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9112 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9113 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9114 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9116 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9119 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9121 SMESHDS_Mesh* meshDS = GetMeshDS();
9122 SMESHDS_SubMesh* smDS = 0;
9123 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9125 const SMDS_MeshElement* elem = *eIt;
9128 int nbCentralNodes = 0;
9129 switch ( elem->GetEntityType() ) {
9130 // linear convertible
9131 case SMDSEntity_Edge:
9132 case SMDSEntity_Triangle:
9133 case SMDSEntity_Quadrangle:
9134 case SMDSEntity_Tetra:
9135 case SMDSEntity_Pyramid:
9136 case SMDSEntity_Hexa:
9137 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9138 // quadratic that can become bi-quadratic
9139 case SMDSEntity_Quad_Triangle:
9140 case SMDSEntity_Quad_Quadrangle:
9141 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9143 case SMDSEntity_BiQuad_Triangle:
9144 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9145 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9147 default: alreadyOK = true;
9149 if ( alreadyOK ) continue;
9151 const SMDSAbs_ElementType type = elem->GetType();
9152 const int id = elem->GetID();
9153 const int nbNodes = elem->NbCornerNodes();
9154 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9156 helper.SetSubShape( elem->getshapeId() );
9158 if ( !smDS || !smDS->Contains( elem ))
9159 smDS = meshDS->MeshElements( elem->getshapeId() );
9160 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9162 SMDS_MeshElement * newElem = 0;
9165 case 4: // cases for most frequently used element types go first (for optimization)
9166 if ( type == SMDSAbs_Volume )
9167 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9169 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9172 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9173 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9176 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9179 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9182 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9183 nodes[4], id, theForce3d);
9186 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9187 nodes[4], nodes[5], id, theForce3d);
9191 ReplaceElemInGroups( elem, newElem, meshDS);
9192 if( newElem && smDS )
9193 smDS->AddElement( newElem );
9195 // remove central nodes
9196 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9197 if ( nodes[i]->NbInverseElements() == 0 )
9198 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9200 } // loop on theElements
9203 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9204 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9205 // helper.FixQuadraticElements( myError );
9206 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9210 //=======================================================================
9212 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9213 * \return int - nb of checked elements
9215 //=======================================================================
9217 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9218 SMDS_ElemIteratorPtr theItr,
9219 const int theShapeID)
9222 SMESHDS_Mesh* meshDS = GetMeshDS();
9223 ElemFeatures elemType;
9224 vector<const SMDS_MeshNode *> nodes;
9226 while( theItr->more() )
9228 const SMDS_MeshElement* elem = theItr->next();
9230 if( elem && elem->IsQuadratic())
9233 int nbCornerNodes = elem->NbCornerNodes();
9234 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9236 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9238 //remove a quadratic element
9239 if ( !theSm || !theSm->Contains( elem ))
9240 theSm = meshDS->MeshElements( elem->getshapeId() );
9241 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9243 // remove medium nodes
9244 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9245 if ( nodes[i]->NbInverseElements() == 0 )
9246 meshDS->RemoveFreeNode( nodes[i], theSm );
9248 // add a linear element
9249 nodes.resize( nbCornerNodes );
9250 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9251 ReplaceElemInGroups(elem, newElem, meshDS);
9252 if( theSm && newElem )
9253 theSm->AddElement( newElem );
9259 //=======================================================================
9260 //function : ConvertFromQuadratic
9262 //=======================================================================
9264 bool SMESH_MeshEditor::ConvertFromQuadratic()
9266 int nbCheckedElems = 0;
9267 if ( myMesh->HasShapeToMesh() )
9269 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9271 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9272 while ( smIt->more() ) {
9273 SMESH_subMesh* sm = smIt->next();
9274 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9275 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9281 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9282 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9284 SMESHDS_SubMesh *aSM = 0;
9285 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9293 //================================================================================
9295 * \brief Return true if all medium nodes of the element are in the node set
9297 //================================================================================
9299 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9301 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9302 if ( !nodeSet.count( elem->GetNode(i) ))
9308 //================================================================================
9310 * \brief Makes given elements linear
9312 //================================================================================
9314 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9316 if ( theElements.empty() ) return;
9318 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9319 set<int> mediumNodeIDs;
9320 TIDSortedElemSet::iterator eIt = theElements.begin();
9321 for ( ; eIt != theElements.end(); ++eIt )
9323 const SMDS_MeshElement* e = *eIt;
9324 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9325 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9328 // replace given elements by linear ones
9329 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9330 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9332 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9333 // except those elements sharing medium nodes of quadratic element whose medium nodes
9334 // are not all in mediumNodeIDs
9336 // get remaining medium nodes
9337 TIDSortedNodeSet mediumNodes;
9338 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9339 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9340 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9341 mediumNodes.insert( mediumNodes.end(), n );
9343 // find more quadratic elements to convert
9344 TIDSortedElemSet moreElemsToConvert;
9345 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9346 for ( ; nIt != mediumNodes.end(); ++nIt )
9348 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9349 while ( invIt->more() )
9351 const SMDS_MeshElement* e = invIt->next();
9352 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9354 // find a more complex element including e and
9355 // whose medium nodes are not in mediumNodes
9356 bool complexFound = false;
9357 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9359 SMDS_ElemIteratorPtr invIt2 =
9360 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9361 while ( invIt2->more() )
9363 const SMDS_MeshElement* eComplex = invIt2->next();
9364 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9366 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9367 if ( nbCommonNodes == e->NbNodes())
9369 complexFound = true;
9370 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9376 if ( !complexFound )
9377 moreElemsToConvert.insert( e );
9381 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9382 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9385 //=======================================================================
9386 //function : SewSideElements
9388 //=======================================================================
9390 SMESH_MeshEditor::Sew_Error
9391 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9392 TIDSortedElemSet& theSide2,
9393 const SMDS_MeshNode* theFirstNode1,
9394 const SMDS_MeshNode* theFirstNode2,
9395 const SMDS_MeshNode* theSecondNode1,
9396 const SMDS_MeshNode* theSecondNode2)
9400 if ( theSide1.size() != theSide2.size() )
9401 return SEW_DIFF_NB_OF_ELEMENTS;
9403 Sew_Error aResult = SEW_OK;
9405 // 1. Build set of faces representing each side
9406 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9407 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9409 // =======================================================================
9410 // 1. Build set of faces representing each side:
9411 // =======================================================================
9412 // a. build set of nodes belonging to faces
9413 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9414 // c. create temporary faces representing side of volumes if correspondent
9415 // face does not exist
9417 SMESHDS_Mesh* aMesh = GetMeshDS();
9418 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9419 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9420 TIDSortedElemSet faceSet1, faceSet2;
9421 set<const SMDS_MeshElement*> volSet1, volSet2;
9422 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9423 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9424 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9425 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9426 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9427 int iSide, iFace, iNode;
9429 list<const SMDS_MeshElement* > tempFaceList;
9430 for ( iSide = 0; iSide < 2; iSide++ ) {
9431 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9432 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9433 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9434 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9435 set<const SMDS_MeshElement*>::iterator vIt;
9436 TIDSortedElemSet::iterator eIt;
9437 set<const SMDS_MeshNode*>::iterator nIt;
9439 // check that given nodes belong to given elements
9440 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9441 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9442 int firstIndex = -1, secondIndex = -1;
9443 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9444 const SMDS_MeshElement* elem = *eIt;
9445 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9446 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9447 if ( firstIndex > -1 && secondIndex > -1 ) break;
9449 if ( firstIndex < 0 || secondIndex < 0 ) {
9450 // we can simply return until temporary faces created
9451 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9454 // -----------------------------------------------------------
9455 // 1a. Collect nodes of existing faces
9456 // and build set of face nodes in order to detect missing
9457 // faces corresponding to sides of volumes
9458 // -----------------------------------------------------------
9460 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9462 // loop on the given element of a side
9463 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9464 //const SMDS_MeshElement* elem = *eIt;
9465 const SMDS_MeshElement* elem = *eIt;
9466 if ( elem->GetType() == SMDSAbs_Face ) {
9467 faceSet->insert( elem );
9468 set <const SMDS_MeshNode*> faceNodeSet;
9469 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9470 while ( nodeIt->more() ) {
9471 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9472 nodeSet->insert( n );
9473 faceNodeSet.insert( n );
9475 setOfFaceNodeSet.insert( faceNodeSet );
9477 else if ( elem->GetType() == SMDSAbs_Volume )
9478 volSet->insert( elem );
9480 // ------------------------------------------------------------------------------
9481 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9482 // ------------------------------------------------------------------------------
9484 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9485 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9486 while ( fIt->more() ) { // loop on faces sharing a node
9487 const SMDS_MeshElement* f = fIt->next();
9488 if ( faceSet->find( f ) == faceSet->end() ) {
9489 // check if all nodes are in nodeSet and
9490 // complete setOfFaceNodeSet if they are
9491 set <const SMDS_MeshNode*> faceNodeSet;
9492 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9493 bool allInSet = true;
9494 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9495 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9496 if ( nodeSet->find( n ) == nodeSet->end() )
9499 faceNodeSet.insert( n );
9502 faceSet->insert( f );
9503 setOfFaceNodeSet.insert( faceNodeSet );
9509 // -------------------------------------------------------------------------
9510 // 1c. Create temporary faces representing sides of volumes if correspondent
9511 // face does not exist
9512 // -------------------------------------------------------------------------
9514 if ( !volSet->empty() ) {
9515 //int nodeSetSize = nodeSet->size();
9517 // loop on given volumes
9518 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9519 SMDS_VolumeTool vol (*vIt);
9520 // loop on volume faces: find free faces
9521 // --------------------------------------
9522 list<const SMDS_MeshElement* > freeFaceList;
9523 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9524 if ( !vol.IsFreeFace( iFace ))
9526 // check if there is already a face with same nodes in a face set
9527 const SMDS_MeshElement* aFreeFace = 0;
9528 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9529 int nbNodes = vol.NbFaceNodes( iFace );
9530 set <const SMDS_MeshNode*> faceNodeSet;
9531 vol.GetFaceNodes( iFace, faceNodeSet );
9532 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9534 // no such a face is given but it still can exist, check it
9535 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9536 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9539 // create a temporary face
9540 if ( nbNodes == 3 ) {
9541 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9542 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9544 else if ( nbNodes == 4 ) {
9545 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9546 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9549 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9550 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9551 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9554 tempFaceList.push_back( aFreeFace );
9558 freeFaceList.push_back( aFreeFace );
9560 } // loop on faces of a volume
9562 // choose one of several free faces of a volume
9563 // --------------------------------------------
9564 if ( freeFaceList.size() > 1 ) {
9565 // choose a face having max nb of nodes shared by other elems of a side
9566 int maxNbNodes = -1;
9567 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9568 while ( fIt != freeFaceList.end() ) { // loop on free faces
9569 int nbSharedNodes = 0;
9570 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9571 while ( nodeIt->more() ) { // loop on free face nodes
9572 const SMDS_MeshNode* n =
9573 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9574 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9575 while ( invElemIt->more() ) {
9576 const SMDS_MeshElement* e = invElemIt->next();
9577 nbSharedNodes += faceSet->count( e );
9578 nbSharedNodes += elemSet->count( e );
9581 if ( nbSharedNodes > maxNbNodes ) {
9582 maxNbNodes = nbSharedNodes;
9583 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9585 else if ( nbSharedNodes == maxNbNodes ) {
9589 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9592 if ( freeFaceList.size() > 1 )
9594 // could not choose one face, use another way
9595 // choose a face most close to the bary center of the opposite side
9596 gp_XYZ aBC( 0., 0., 0. );
9597 set <const SMDS_MeshNode*> addedNodes;
9598 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9599 eIt = elemSet2->begin();
9600 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9601 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9602 while ( nodeIt->more() ) { // loop on free face nodes
9603 const SMDS_MeshNode* n =
9604 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9605 if ( addedNodes.insert( n ).second )
9606 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9609 aBC /= addedNodes.size();
9610 double minDist = DBL_MAX;
9611 fIt = freeFaceList.begin();
9612 while ( fIt != freeFaceList.end() ) { // loop on free faces
9614 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9615 while ( nodeIt->more() ) { // loop on free face nodes
9616 const SMDS_MeshNode* n =
9617 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9618 gp_XYZ p( n->X(),n->Y(),n->Z() );
9619 dist += ( aBC - p ).SquareModulus();
9621 if ( dist < minDist ) {
9623 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9626 fIt = freeFaceList.erase( fIt++ );
9629 } // choose one of several free faces of a volume
9631 if ( freeFaceList.size() == 1 ) {
9632 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9633 faceSet->insert( aFreeFace );
9634 // complete a node set with nodes of a found free face
9635 // for ( iNode = 0; iNode < ; iNode++ )
9636 // nodeSet->insert( fNodes[ iNode ] );
9639 } // loop on volumes of a side
9641 // // complete a set of faces if new nodes in a nodeSet appeared
9642 // // ----------------------------------------------------------
9643 // if ( nodeSetSize != nodeSet->size() ) {
9644 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9645 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9646 // while ( fIt->more() ) { // loop on faces sharing a node
9647 // const SMDS_MeshElement* f = fIt->next();
9648 // if ( faceSet->find( f ) == faceSet->end() ) {
9649 // // check if all nodes are in nodeSet and
9650 // // complete setOfFaceNodeSet if they are
9651 // set <const SMDS_MeshNode*> faceNodeSet;
9652 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9653 // bool allInSet = true;
9654 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9655 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9656 // if ( nodeSet->find( n ) == nodeSet->end() )
9657 // allInSet = false;
9659 // faceNodeSet.insert( n );
9661 // if ( allInSet ) {
9662 // faceSet->insert( f );
9663 // setOfFaceNodeSet.insert( faceNodeSet );
9669 } // Create temporary faces, if there are volumes given
9672 if ( faceSet1.size() != faceSet2.size() ) {
9673 // delete temporary faces: they are in reverseElements of actual nodes
9674 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9675 // while ( tmpFaceIt->more() )
9676 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9677 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9678 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9679 // aMesh->RemoveElement(*tmpFaceIt);
9680 MESSAGE("Diff nb of faces");
9681 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9684 // ============================================================
9685 // 2. Find nodes to merge:
9686 // bind a node to remove to a node to put instead
9687 // ============================================================
9689 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9690 if ( theFirstNode1 != theFirstNode2 )
9691 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9692 if ( theSecondNode1 != theSecondNode2 )
9693 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9695 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9696 set< long > linkIdSet; // links to process
9697 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9699 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9700 list< NLink > linkList[2];
9701 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9702 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9703 // loop on links in linkList; find faces by links and append links
9704 // of the found faces to linkList
9705 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9706 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9708 NLink link[] = { *linkIt[0], *linkIt[1] };
9709 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9710 if ( !linkIdSet.count( linkID ) )
9713 // by links, find faces in the face sets,
9714 // and find indices of link nodes in the found faces;
9715 // in a face set, there is only one or no face sharing a link
9716 // ---------------------------------------------------------------
9718 const SMDS_MeshElement* face[] = { 0, 0 };
9719 vector<const SMDS_MeshNode*> fnodes[2];
9720 int iLinkNode[2][2];
9721 TIDSortedElemSet avoidSet;
9722 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9723 const SMDS_MeshNode* n1 = link[iSide].first;
9724 const SMDS_MeshNode* n2 = link[iSide].second;
9725 //cout << "Side " << iSide << " ";
9726 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9727 // find a face by two link nodes
9728 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9729 *faceSetPtr[ iSide ], avoidSet,
9730 &iLinkNode[iSide][0],
9731 &iLinkNode[iSide][1] );
9734 //cout << " F " << face[ iSide]->GetID() <<endl;
9735 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9736 // put face nodes to fnodes
9737 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9738 fnodes[ iSide ].assign( nIt, nEnd );
9739 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9743 // check similarity of elements of the sides
9744 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9745 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9746 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9747 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9750 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9752 break; // do not return because it's necessary to remove tmp faces
9755 // set nodes to merge
9756 // -------------------
9758 if ( face[0] && face[1] ) {
9759 const int nbNodes = face[0]->NbNodes();
9760 if ( nbNodes != face[1]->NbNodes() ) {
9761 MESSAGE("Diff nb of face nodes");
9762 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9763 break; // do not return because it s necessary to remove tmp faces
9765 bool reverse[] = { false, false }; // order of nodes in the link
9766 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9767 // analyse link orientation in faces
9768 int i1 = iLinkNode[ iSide ][ 0 ];
9769 int i2 = iLinkNode[ iSide ][ 1 ];
9770 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9772 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9773 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9774 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9776 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9777 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9780 // add other links of the faces to linkList
9781 // -----------------------------------------
9783 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9784 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9785 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9786 if ( !iter_isnew.second ) { // already in a set: no need to process
9787 linkIdSet.erase( iter_isnew.first );
9789 else // new in set == encountered for the first time: add
9791 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9792 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9793 linkList[0].push_back ( NLink( n1, n2 ));
9794 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9799 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9802 } // loop on link lists
9804 if ( aResult == SEW_OK &&
9805 ( //linkIt[0] != linkList[0].end() ||
9806 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9807 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9808 " " << (faceSetPtr[1]->empty()));
9809 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9812 // ====================================================================
9813 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9814 // ====================================================================
9816 // delete temporary faces
9817 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9818 // while ( tmpFaceIt->more() )
9819 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9820 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9821 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9822 aMesh->RemoveElement(*tmpFaceIt);
9824 if ( aResult != SEW_OK)
9827 list< int > nodeIDsToRemove;
9828 vector< const SMDS_MeshNode*> nodes;
9829 ElemFeatures elemType;
9831 // loop on nodes replacement map
9832 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9833 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9834 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9836 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9837 nodeIDsToRemove.push_back( nToRemove->GetID() );
9838 // loop on elements sharing nToRemove
9839 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9840 while ( invElemIt->more() ) {
9841 const SMDS_MeshElement* e = invElemIt->next();
9842 // get a new suite of nodes: make replacement
9843 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9844 nodes.resize( nbNodes );
9845 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9846 while ( nIt->more() ) {
9847 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9848 nnIt = nReplaceMap.find( n );
9849 if ( nnIt != nReplaceMap.end() ) {
9855 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9856 // elemIDsToRemove.push_back( e->GetID() );
9860 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9861 aMesh->RemoveElement( e );
9863 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9865 AddToSameGroups( newElem, e, aMesh );
9866 if ( int aShapeId = e->getshapeId() )
9867 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9873 Remove( nodeIDsToRemove, true );
9878 //================================================================================
9880 * \brief Find corresponding nodes in two sets of faces
9881 * \param theSide1 - first face set
9882 * \param theSide2 - second first face
9883 * \param theFirstNode1 - a boundary node of set 1
9884 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9885 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9886 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9887 * \param nReplaceMap - output map of corresponding nodes
9888 * \return bool - is a success or not
9890 //================================================================================
9893 //#define DEBUG_MATCHING_NODES
9896 SMESH_MeshEditor::Sew_Error
9897 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9898 set<const SMDS_MeshElement*>& theSide2,
9899 const SMDS_MeshNode* theFirstNode1,
9900 const SMDS_MeshNode* theFirstNode2,
9901 const SMDS_MeshNode* theSecondNode1,
9902 const SMDS_MeshNode* theSecondNode2,
9903 TNodeNodeMap & nReplaceMap)
9905 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9907 nReplaceMap.clear();
9908 //if ( theFirstNode1 != theFirstNode2 )
9909 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9910 //if ( theSecondNode1 != theSecondNode2 )
9911 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9913 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9914 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9916 list< NLink > linkList[2];
9917 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9918 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9920 // loop on links in linkList; find faces by links and append links
9921 // of the found faces to linkList
9922 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9923 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9924 NLink link[] = { *linkIt[0], *linkIt[1] };
9925 if ( linkSet.find( link[0] ) == linkSet.end() )
9928 // by links, find faces in the face sets,
9929 // and find indices of link nodes in the found faces;
9930 // in a face set, there is only one or no face sharing a link
9931 // ---------------------------------------------------------------
9933 const SMDS_MeshElement* face[] = { 0, 0 };
9934 list<const SMDS_MeshNode*> notLinkNodes[2];
9935 //bool reverse[] = { false, false }; // order of notLinkNodes
9937 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9939 const SMDS_MeshNode* n1 = link[iSide].first;
9940 const SMDS_MeshNode* n2 = link[iSide].second;
9941 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9942 set< const SMDS_MeshElement* > facesOfNode1;
9943 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9945 // during a loop of the first node, we find all faces around n1,
9946 // during a loop of the second node, we find one face sharing both n1 and n2
9947 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9948 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9949 while ( fIt->more() ) { // loop on faces sharing a node
9950 const SMDS_MeshElement* f = fIt->next();
9951 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9952 ! facesOfNode1.insert( f ).second ) // f encounters twice
9954 if ( face[ iSide ] ) {
9955 MESSAGE( "2 faces per link " );
9956 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9959 faceSet->erase( f );
9961 // get not link nodes
9962 int nbN = f->NbNodes();
9963 if ( f->IsQuadratic() )
9965 nbNodes[ iSide ] = nbN;
9966 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9967 int i1 = f->GetNodeIndex( n1 );
9968 int i2 = f->GetNodeIndex( n2 );
9969 int iEnd = nbN, iBeg = -1, iDelta = 1;
9970 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9972 std::swap( iEnd, iBeg ); iDelta = -1;
9977 if ( i == iEnd ) i = iBeg + iDelta;
9978 if ( i == i1 ) break;
9979 nodes.push_back ( f->GetNode( i ) );
9985 // check similarity of elements of the sides
9986 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9987 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9988 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9989 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9992 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9996 // set nodes to merge
9997 // -------------------
9999 if ( face[0] && face[1] ) {
10000 if ( nbNodes[0] != nbNodes[1] ) {
10001 MESSAGE("Diff nb of face nodes");
10002 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10004 #ifdef DEBUG_MATCHING_NODES
10005 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10006 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10007 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10009 int nbN = nbNodes[0];
10011 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10012 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10013 for ( int i = 0 ; i < nbN - 2; ++i ) {
10014 #ifdef DEBUG_MATCHING_NODES
10015 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10017 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10021 // add other links of the face 1 to linkList
10022 // -----------------------------------------
10024 const SMDS_MeshElement* f0 = face[0];
10025 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10026 for ( int i = 0; i < nbN; i++ )
10028 const SMDS_MeshNode* n2 = f0->GetNode( i );
10029 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10030 linkSet.insert( SMESH_TLink( n1, n2 ));
10031 if ( !iter_isnew.second ) { // already in a set: no need to process
10032 linkSet.erase( iter_isnew.first );
10034 else // new in set == encountered for the first time: add
10036 #ifdef DEBUG_MATCHING_NODES
10037 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10038 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10040 linkList[0].push_back ( NLink( n1, n2 ));
10041 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10046 } // loop on link lists
10051 namespace // automatically find theAffectedElems for DoubleNodes()
10053 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10055 //--------------------------------------------------------------------------------
10056 // Nodes shared by adjacent FissureBorder's.
10057 // 1 node if FissureBorder separates faces
10058 // 2 nodes if FissureBorder separates volumes
10061 const SMDS_MeshNode* _nodes[2];
10064 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10068 _nbNodes = bool( n1 ) + bool( n2 );
10069 if ( _nbNodes == 2 && n1 > n2 )
10070 std::swap( _nodes[0], _nodes[1] );
10072 bool operator<( const SubBorder& other ) const
10074 for ( int i = 0; i < _nbNodes; ++i )
10076 if ( _nodes[i] < other._nodes[i] ) return true;
10077 if ( _nodes[i] > other._nodes[i] ) return false;
10083 //--------------------------------------------------------------------------------
10084 // Map a SubBorder to all FissureBorder it bounds
10085 struct FissureBorder;
10086 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10087 typedef TBorderLinks::iterator TMappedSub;
10089 //--------------------------------------------------------------------------------
10091 * \brief Element border (volume facet or face edge) at a fissure
10093 struct FissureBorder
10095 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10096 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10098 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10099 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10101 FissureBorder( FissureBorder && from ) // move constructor
10103 std::swap( _nodes, from._nodes );
10104 std::swap( _sortedNodes, from._sortedNodes );
10105 _elems[0] = from._elems[0];
10106 _elems[1] = from._elems[1];
10109 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10110 std::vector< const SMDS_MeshElement* > & adjElems)
10111 : _nodes( elemToDuplicate->NbCornerNodes() )
10113 for ( size_t i = 0; i < _nodes.size(); ++i )
10114 _nodes[i] = elemToDuplicate->GetNode( i );
10116 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10117 findAdjacent( type, adjElems );
10120 FissureBorder( const SMDS_MeshNode** nodes,
10121 const size_t nbNodes,
10122 const SMDSAbs_ElementType adjElemsType,
10123 std::vector< const SMDS_MeshElement* > & adjElems)
10124 : _nodes( nodes, nodes + nbNodes )
10126 findAdjacent( adjElemsType, adjElems );
10129 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10130 std::vector< const SMDS_MeshElement* > & adjElems)
10132 _elems[0] = _elems[1] = 0;
10134 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10135 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10136 _elems[i] = adjElems[i];
10139 bool operator<( const FissureBorder& other ) const
10141 return GetSortedNodes() < other.GetSortedNodes();
10144 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10146 if ( _sortedNodes.empty() && !_nodes.empty() )
10148 FissureBorder* me = const_cast<FissureBorder*>( this );
10149 me->_sortedNodes = me->_nodes;
10150 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10152 return _sortedNodes;
10155 size_t NbSub() const
10157 return _nodes.size();
10160 SubBorder Sub(size_t i) const
10162 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10165 void AddSelfTo( TBorderLinks& borderLinks )
10167 _mappedSubs.resize( NbSub() );
10168 for ( size_t i = 0; i < NbSub(); ++i )
10170 TBorderLinks::iterator s2b =
10171 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10172 s2b->second.push_back( this );
10173 _mappedSubs[ i ] = s2b;
10182 const SMDS_MeshElement* GetMarkedElem() const
10184 if ( _nodes.empty() ) return 0; // cleared
10185 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10186 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10190 gp_XYZ GetNorm() const // normal to the border
10193 if ( _nodes.size() == 2 )
10195 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10196 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10198 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10201 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10202 norm = bordDir ^ avgNorm;
10206 SMESH_NodeXYZ p0( _nodes[0] );
10207 SMESH_NodeXYZ p1( _nodes[1] );
10208 SMESH_NodeXYZ p2( _nodes[2] );
10209 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10211 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10217 void ChooseSide() // mark an _elem located at positive side of fissure
10219 _elems[0]->setIsMarked( true );
10220 gp_XYZ norm = GetNorm();
10221 double maxX = norm.Coord(1);
10222 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10223 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10226 _elems[0]->setIsMarked( false );
10227 _elems[1]->setIsMarked( true );
10231 }; // struct FissureBorder
10233 //--------------------------------------------------------------------------------
10235 * \brief Classifier of elements at fissure edge
10237 class FissureNormal
10239 std::vector< gp_XYZ > _normals;
10243 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10246 _normals.reserve(2);
10247 _normals.push_back( bord.GetNorm() );
10248 if ( _normals.size() == 2 )
10249 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10252 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10255 switch ( _normals.size() ) {
10258 isIn = !isOut( n, _normals[0], elem );
10263 bool in1 = !isOut( n, _normals[0], elem );
10264 bool in2 = !isOut( n, _normals[1], elem );
10265 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10272 //================================================================================
10274 * \brief Classify an element by a plane passing through a node
10276 //================================================================================
10278 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10280 SMESH_NodeXYZ p = n;
10282 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10284 SMESH_NodeXYZ pi = elem->GetNode( i );
10285 sumDot += norm * ( pi - p );
10287 return sumDot < -1e-100;
10290 //================================================================================
10292 * \brief Find FissureBorder's by nodes to duplicate
10294 //================================================================================
10296 void findFissureBorders( const TIDSortedElemSet& theNodes,
10297 std::vector< FissureBorder > & theFissureBorders )
10299 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10300 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10302 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10303 if ( n->NbInverseElements( elemType ) == 0 )
10305 elemType = SMDSAbs_Face;
10306 if ( n->NbInverseElements( elemType ) == 0 )
10309 // unmark elements touching the fissure
10310 for ( ; nIt != theNodes.end(); ++nIt )
10311 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10313 // loop on elements touching the fissure to get their borders belonging to the fissure
10314 std::set< FissureBorder > fissureBorders;
10315 std::vector< const SMDS_MeshElement* > adjElems;
10316 std::vector< const SMDS_MeshNode* > nodes;
10317 SMDS_VolumeTool volTool;
10318 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10320 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10321 while ( invIt->more() )
10323 const SMDS_MeshElement* eInv = invIt->next();
10324 if ( eInv->isMarked() ) continue;
10325 eInv->setIsMarked( true );
10327 if ( elemType == SMDSAbs_Volume )
10329 volTool.Set( eInv );
10330 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10331 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10333 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10334 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10336 bool allOnFissure = true;
10337 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10338 if (( allOnFissure = theNodes.count( nn[ iN ])))
10339 nodes.push_back( nn[ iN ]);
10340 if ( allOnFissure )
10341 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10342 elemType, adjElems )));
10345 else // elemType == SMDSAbs_Face
10347 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10348 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10349 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10351 nn[1] = eInv->GetNode( iN );
10352 onFissure1 = theNodes.count( nn[1] );
10353 if ( onFissure0 && onFissure1 )
10354 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10356 onFissure0 = onFissure1;
10362 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10363 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10364 for ( ; bord != fissureBorders.end(); ++bord )
10366 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10369 } // findFissureBorders()
10371 //================================================================================
10373 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10374 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10375 * \param [in] theNodesNot - nodes not to duplicate
10376 * \param [out] theAffectedElems - the found elements
10378 //================================================================================
10380 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10381 TIDSortedElemSet& theAffectedElems)
10383 if ( theElemsOrNodes.empty() ) return;
10385 // find FissureBorder's
10387 std::vector< FissureBorder > fissure;
10388 std::vector< const SMDS_MeshElement* > elemsByFacet;
10390 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10391 if ( (*elIt)->GetType() == SMDSAbs_Node )
10393 findFissureBorders( theElemsOrNodes, fissure );
10397 fissure.reserve( theElemsOrNodes.size() );
10398 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10399 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10401 if ( fissure.empty() )
10404 // fill borderLinks
10406 TBorderLinks borderLinks;
10408 for ( size_t i = 0; i < fissure.size(); ++i )
10410 fissure[i].AddSelfTo( borderLinks );
10413 // get theAffectedElems
10415 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10416 for ( size_t i = 0; i < fissure.size(); ++i )
10417 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10419 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10420 false, /*markElem=*/true );
10423 std::vector<const SMDS_MeshNode *> facetNodes;
10424 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10425 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10427 // choose a side of fissure
10428 fissure[0].ChooseSide();
10429 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10431 size_t nbCheckedBorders = 0;
10432 while ( nbCheckedBorders < fissure.size() )
10434 // find a FissureBorder to treat
10435 FissureBorder* bord = 0;
10436 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10437 if ( fissure[i].GetMarkedElem() )
10438 bord = & fissure[i];
10439 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10440 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10442 bord = & fissure[i];
10443 bord->ChooseSide();
10444 theAffectedElems.insert( bord->GetMarkedElem() );
10446 if ( !bord ) return;
10447 ++nbCheckedBorders;
10449 // treat FissureBorder's linked to bord
10450 fissureNodes.clear();
10451 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10452 for ( size_t i = 0; i < bord->NbSub(); ++i )
10454 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10455 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10456 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10457 const SubBorder& sb = l2b->first;
10458 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10460 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10462 for ( int j = 0; j < sb._nbNodes; ++j )
10463 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10467 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10468 // until an elem adjacent to a neighbour FissureBorder is found
10469 facetNodes.clear();
10470 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10471 facetNodes.resize( sb._nbNodes + 1 );
10475 // check if bordElem is adjacent to a neighbour FissureBorder
10476 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10478 FissureBorder* bord2 = linkedBorders[j];
10479 if ( bord2 == bord ) continue;
10480 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10483 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10488 // find the next bordElem
10489 const SMDS_MeshElement* nextBordElem = 0;
10490 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10492 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10493 if ( fissureNodes.count( n )) continue;
10495 facetNodes[ sb._nbNodes ] = n;
10496 elemsByFacet.clear();
10497 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10499 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10500 if ( elemsByFacet[ iE ] != bordElem &&
10501 !elemsByFacet[ iE ]->isMarked() )
10503 theAffectedElems.insert( elemsByFacet[ iE ]);
10504 elemsByFacet[ iE ]->setIsMarked( true );
10505 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10506 nextBordElem = elemsByFacet[ iE ];
10510 bordElem = nextBordElem;
10512 } // while ( bordElem )
10514 linkedBorders.clear(); // not to treat this link any more
10516 } // loop on SubBorder's of a FissureBorder
10520 } // loop on FissureBorder's
10523 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10525 // mark nodes of theAffectedElems
10526 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10528 // unmark nodes of the fissure
10529 elIt = theElemsOrNodes.begin();
10530 if ( (*elIt)->GetType() == SMDSAbs_Node )
10531 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10533 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10535 std::vector< gp_XYZ > normVec;
10537 // loop on nodes of the fissure, add elements having marked nodes
10538 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10540 const SMDS_MeshElement* e = (*elIt);
10541 if ( e->GetType() != SMDSAbs_Node )
10542 e->setIsMarked( true ); // avoid adding a fissure element
10544 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10546 const SMDS_MeshNode* n = e->GetNode( iN );
10547 if ( fissEdgeNodes2Norm.count( n ))
10550 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10551 while ( invIt->more() )
10553 const SMDS_MeshElement* eInv = invIt->next();
10554 if ( eInv->isMarked() ) continue;
10555 eInv->setIsMarked( true );
10557 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10558 while( nIt->more() )
10559 if ( nIt->next()->isMarked())
10561 theAffectedElems.insert( eInv );
10562 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10563 n->setIsMarked( false );
10570 // add elements on the fissure edge
10571 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10572 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10574 const SMDS_MeshNode* edgeNode = n2N->first;
10575 const FissureNormal & normals = n2N->second;
10577 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10578 while ( invIt->more() )
10580 const SMDS_MeshElement* eInv = invIt->next();
10581 if ( eInv->isMarked() ) continue;
10582 eInv->setIsMarked( true );
10584 // classify eInv using normals
10585 bool toAdd = normals.IsIn( edgeNode, eInv );
10586 if ( toAdd ) // check if all nodes lie on the fissure edge
10588 bool notOnEdge = false;
10589 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10590 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10595 theAffectedElems.insert( eInv );
10601 } // findAffectedElems()
10604 //================================================================================
10606 * \brief Create elements equal (on same nodes) to given ones
10607 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10608 * elements of the uppest dimension are duplicated.
10610 //================================================================================
10612 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10614 ClearLastCreated();
10615 SMESHDS_Mesh* mesh = GetMeshDS();
10617 // get an element type and an iterator over elements
10619 SMDSAbs_ElementType type = SMDSAbs_All;
10620 SMDS_ElemIteratorPtr elemIt;
10621 if ( theElements.empty() )
10623 if ( mesh->NbNodes() == 0 )
10625 // get most complex type
10626 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10627 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10628 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10630 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10631 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10634 elemIt = mesh->elementsIterator( type );
10640 //type = (*theElements.begin())->GetType();
10641 elemIt = SMESHUtils::elemSetIterator( theElements );
10644 // un-mark all elements to avoid duplicating just created elements
10645 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10647 // duplicate elements
10649 ElemFeatures elemType;
10651 vector< const SMDS_MeshNode* > nodes;
10652 while ( elemIt->more() )
10654 const SMDS_MeshElement* elem = elemIt->next();
10655 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10656 ( elem->isMarked() ))
10659 elemType.Init( elem, /*basicOnly=*/false );
10660 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10662 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10663 newElem->setIsMarked( true );
10667 //================================================================================
10669 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10670 \param theElems - the list of elements (edges or faces) to be replicated
10671 The nodes for duplication could be found from these elements
10672 \param theNodesNot - list of nodes to NOT replicate
10673 \param theAffectedElems - the list of elements (cells and edges) to which the
10674 replicated nodes should be associated to.
10675 \return TRUE if operation has been completed successfully, FALSE otherwise
10677 //================================================================================
10679 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10680 const TIDSortedElemSet& theNodesNot,
10681 const TIDSortedElemSet& theAffectedElems )
10683 ClearLastCreated();
10685 if ( theElems.size() == 0 )
10688 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10693 TNodeNodeMap anOldNodeToNewNode;
10694 // duplicate elements and nodes
10695 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10696 // replce nodes by duplications
10697 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10701 //================================================================================
10703 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704 \param theMeshDS - mesh instance
10705 \param theElems - the elements replicated or modified (nodes should be changed)
10706 \param theNodesNot - nodes to NOT replicate
10707 \param theNodeNodeMap - relation of old node to new created node
10708 \param theIsDoubleElem - flag os to replicate element or modify
10709 \return TRUE if operation has been completed successfully, FALSE otherwise
10711 //================================================================================
10713 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10714 const TIDSortedElemSet& theElems,
10715 const TIDSortedElemSet& theNodesNot,
10716 TNodeNodeMap& theNodeNodeMap,
10717 const bool theIsDoubleElem )
10719 // iterate through element and duplicate them (by nodes duplication)
10721 std::vector<const SMDS_MeshNode*> newNodes;
10722 ElemFeatures elemType;
10724 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10725 for ( ; elemItr != theElems.end(); ++elemItr )
10727 const SMDS_MeshElement* anElem = *elemItr;
10731 // duplicate nodes to duplicate element
10732 bool isDuplicate = false;
10733 newNodes.resize( anElem->NbNodes() );
10734 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10736 while ( anIter->more() )
10738 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10739 const SMDS_MeshNode* aNewNode = aCurrNode;
10740 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10741 if ( n2n != theNodeNodeMap.end() )
10743 aNewNode = n2n->second;
10745 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10748 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10749 copyPosition( aCurrNode, aNewNode );
10750 theNodeNodeMap[ aCurrNode ] = aNewNode;
10751 myLastCreatedNodes.push_back( aNewNode );
10753 isDuplicate |= (aCurrNode != aNewNode);
10754 newNodes[ ind++ ] = aNewNode;
10756 if ( !isDuplicate )
10759 if ( theIsDoubleElem )
10760 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10762 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10769 //================================================================================
10771 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10772 \param theNodes - identifiers of nodes to be doubled
10773 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10774 nodes. If list of element identifiers is empty then nodes are doubled but
10775 they not assigned to elements
10776 \return TRUE if operation has been completed successfully, FALSE otherwise
10778 //================================================================================
10780 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10781 const std::list< int >& theListOfModifiedElems )
10783 ClearLastCreated();
10785 if ( theListOfNodes.size() == 0 )
10788 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10792 // iterate through nodes and duplicate them
10794 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10796 std::list< int >::const_iterator aNodeIter;
10797 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10799 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10805 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10808 copyPosition( aNode, aNewNode );
10809 anOldNodeToNewNode[ aNode ] = aNewNode;
10810 myLastCreatedNodes.push_back( aNewNode );
10814 // Change nodes of elements
10816 std::vector<const SMDS_MeshNode*> aNodeArr;
10818 std::list< int >::const_iterator anElemIter;
10819 for ( anElemIter = theListOfModifiedElems.begin();
10820 anElemIter != theListOfModifiedElems.end();
10823 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10827 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10828 for( size_t i = 0; i < aNodeArr.size(); ++i )
10830 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10831 anOldNodeToNewNode.find( aNodeArr[ i ]);
10832 if ( n2n != anOldNodeToNewNode.end() )
10833 aNodeArr[ i ] = n2n->second;
10835 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10843 //================================================================================
10845 \brief Check if element located inside shape
10846 \return TRUE if IN or ON shape, FALSE otherwise
10848 //================================================================================
10850 template<class Classifier>
10851 bool isInside(const SMDS_MeshElement* theElem,
10852 Classifier& theClassifier,
10853 const double theTol)
10855 gp_XYZ centerXYZ (0, 0, 0);
10856 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10857 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10859 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10860 theClassifier.Perform(aPnt, theTol);
10861 TopAbs_State aState = theClassifier.State();
10862 return (aState == TopAbs_IN || aState == TopAbs_ON );
10865 //================================================================================
10867 * \brief Classifier of the 3D point on the TopoDS_Face
10868 * with interaface suitable for isInside()
10870 //================================================================================
10872 struct _FaceClassifier
10874 Extrema_ExtPS _extremum;
10875 BRepAdaptor_Surface _surface;
10876 TopAbs_State _state;
10878 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10880 _extremum.Initialize( _surface,
10881 _surface.FirstUParameter(), _surface.LastUParameter(),
10882 _surface.FirstVParameter(), _surface.LastVParameter(),
10883 _surface.Tolerance(), _surface.Tolerance() );
10885 void Perform(const gp_Pnt& aPnt, double theTol)
10888 _state = TopAbs_OUT;
10889 _extremum.Perform(aPnt);
10890 if ( _extremum.IsDone() )
10891 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10892 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10894 TopAbs_State State() const
10901 //================================================================================
10903 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10904 This method is the first step of DoubleNodeElemGroupsInRegion.
10905 \param theElems - list of groups of elements (edges or faces) to be replicated
10906 \param theNodesNot - list of groups of nodes not to replicated
10907 \param theShape - shape to detect affected elements (element which geometric center
10908 located on or inside shape). If the shape is null, detection is done on faces orientations
10909 (select elements with a gravity center on the side given by faces normals).
10910 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10911 The replicated nodes should be associated to affected elements.
10913 \sa DoubleNodeElemGroupsInRegion()
10915 //================================================================================
10917 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10918 const TIDSortedElemSet& theNodesNot,
10919 const TopoDS_Shape& theShape,
10920 TIDSortedElemSet& theAffectedElems)
10922 if ( theShape.IsNull() )
10924 findAffectedElems( theElems, theAffectedElems );
10928 const double aTol = Precision::Confusion();
10929 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10930 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10931 if ( theShape.ShapeType() == TopAbs_SOLID )
10933 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10934 bsc3d->PerformInfinitePoint(aTol);
10936 else if (theShape.ShapeType() == TopAbs_FACE )
10938 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10941 // iterates on indicated elements and get elements by back references from their nodes
10942 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10943 for ( ; elemItr != theElems.end(); ++elemItr )
10945 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10946 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10947 while ( nodeItr->more() )
10949 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10950 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10952 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10953 while ( backElemItr->more() )
10955 const SMDS_MeshElement* curElem = backElemItr->next();
10956 if ( curElem && theElems.find(curElem) == theElems.end() &&
10958 isInside( curElem, *bsc3d, aTol ) :
10959 isInside( curElem, *aFaceClassifier, aTol )))
10960 theAffectedElems.insert( curElem );
10968 //================================================================================
10970 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10971 \param theElems - group of of elements (edges or faces) to be replicated
10972 \param theNodesNot - group of nodes not to replicate
10973 \param theShape - shape to detect affected elements (element which geometric center
10974 located on or inside shape).
10975 The replicated nodes should be associated to affected elements.
10976 \return TRUE if operation has been completed successfully, FALSE otherwise
10978 //================================================================================
10980 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10981 const TIDSortedElemSet& theNodesNot,
10982 const TopoDS_Shape& theShape )
10984 if ( theShape.IsNull() )
10987 const double aTol = Precision::Confusion();
10988 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10989 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10990 if ( theShape.ShapeType() == TopAbs_SOLID )
10992 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10993 bsc3d->PerformInfinitePoint(aTol);
10995 else if (theShape.ShapeType() == TopAbs_FACE )
10997 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11000 // iterates on indicated elements and get elements by back references from their nodes
11001 TIDSortedElemSet anAffected;
11002 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11003 for ( ; elemItr != theElems.end(); ++elemItr )
11005 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11009 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11010 while ( nodeItr->more() )
11012 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11013 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11015 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11016 while ( backElemItr->more() )
11018 const SMDS_MeshElement* curElem = backElemItr->next();
11019 if ( curElem && theElems.find(curElem) == theElems.end() &&
11021 isInside( curElem, *bsc3d, aTol ) :
11022 isInside( curElem, *aFaceClassifier, aTol )))
11023 anAffected.insert( curElem );
11027 return DoubleNodes( theElems, theNodesNot, anAffected );
11031 * \brief compute an oriented angle between two planes defined by four points.
11032 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11033 * @param p0 base of the rotation axe
11034 * @param p1 extremity of the rotation axe
11035 * @param g1 belongs to the first plane
11036 * @param g2 belongs to the second plane
11038 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11040 gp_Vec vref(p0, p1);
11043 gp_Vec n1 = vref.Crossed(v1);
11044 gp_Vec n2 = vref.Crossed(v2);
11046 return n2.AngleWithRef(n1, vref);
11048 catch ( Standard_Failure ) {
11050 return Max( v1.Magnitude(), v2.Magnitude() );
11054 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11055 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11056 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11057 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11058 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11059 * 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.
11060 * 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.
11061 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11062 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11063 * \param theElems - list of groups of volumes, where a group of volume is a set of
11064 * SMDS_MeshElements sorted by Id.
11065 * \param createJointElems - if TRUE, create the elements
11066 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11067 * the boundary between \a theDomains and the rest mesh
11068 * \return TRUE if operation has been completed successfully, FALSE otherwise
11070 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11071 bool createJointElems,
11072 bool onAllBoundaries)
11074 // MESSAGE("----------------------------------------------");
11075 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11076 // MESSAGE("----------------------------------------------");
11078 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11079 meshDS->BuildDownWardConnectivity(true);
11081 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11083 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11084 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11085 // build the list of nodes shared by 2 or more domains, with their domain indexes
11087 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11088 std::map<int,int>celldom; // cell vtkId --> domain
11089 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11090 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11091 faceDomains.clear();
11093 cellDomains.clear();
11094 nodeDomains.clear();
11095 std::map<int,int> emptyMap;
11096 std::set<int> emptySet;
11099 //MESSAGE(".. Number of domains :"<<theElems.size());
11101 TIDSortedElemSet theRestDomElems;
11102 const int iRestDom = -1;
11103 const int idom0 = onAllBoundaries ? iRestDom : 0;
11104 const int nbDomains = theElems.size();
11106 // Check if the domains do not share an element
11107 for (int idom = 0; idom < nbDomains-1; idom++)
11109 // MESSAGE("... Check of domain #" << idom);
11110 const TIDSortedElemSet& domain = theElems[idom];
11111 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11112 for (; elemItr != domain.end(); ++elemItr)
11114 const SMDS_MeshElement* anElem = *elemItr;
11115 int idombisdeb = idom + 1 ;
11116 // check if the element belongs to a domain further in the list
11117 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11119 const TIDSortedElemSet& domainbis = theElems[idombis];
11120 if ( domainbis.count( anElem ))
11122 MESSAGE(".... Domain #" << idom);
11123 MESSAGE(".... Domain #" << idombis);
11124 throw SALOME_Exception("The domains are not disjoint.");
11131 for (int idom = 0; idom < nbDomains; idom++)
11134 // --- build a map (face to duplicate --> volume to modify)
11135 // with all the faces shared by 2 domains (group of elements)
11136 // and corresponding volume of this domain, for each shared face.
11137 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11139 //MESSAGE("... Neighbors of domain #" << idom);
11140 const TIDSortedElemSet& domain = theElems[idom];
11141 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11142 for (; elemItr != domain.end(); ++elemItr)
11144 const SMDS_MeshElement* anElem = *elemItr;
11147 int vtkId = anElem->GetVtkID();
11148 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11149 int neighborsVtkIds[NBMAXNEIGHBORS];
11150 int downIds[NBMAXNEIGHBORS];
11151 unsigned char downTypes[NBMAXNEIGHBORS];
11152 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11153 for (int n = 0; n < nbNeighbors; n++)
11155 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11156 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11157 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11160 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11162 // MESSAGE("Domain " << idombis);
11163 const TIDSortedElemSet& domainbis = theElems[idombis];
11164 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11166 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11168 DownIdType face(downIds[n], downTypes[n]);
11169 if (!faceDomains[face].count(idom))
11171 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11172 celldom[vtkId] = idom;
11173 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11177 theRestDomElems.insert( elem );
11178 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11179 celldom[neighborsVtkIds[n]] = iRestDom;
11187 //MESSAGE("Number of shared faces " << faceDomains.size());
11188 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11190 // --- explore the shared faces domain by domain,
11191 // explore the nodes of the face and see if they belong to a cell in the domain,
11192 // which has only a node or an edge on the border (not a shared face)
11194 for (int idomain = idom0; idomain < nbDomains; idomain++)
11196 //MESSAGE("Domain " << idomain);
11197 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11198 itface = faceDomains.begin();
11199 for (; itface != faceDomains.end(); ++itface)
11201 const std::map<int, int>& domvol = itface->second;
11202 if (!domvol.count(idomain))
11204 DownIdType face = itface->first;
11205 //MESSAGE(" --- face " << face.cellId);
11206 std::set<int> oldNodes;
11208 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11209 std::set<int>::iterator itn = oldNodes.begin();
11210 for (; itn != oldNodes.end(); ++itn)
11213 //MESSAGE(" node " << oldId);
11214 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11215 for (int i=0; i<l.ncells; i++)
11217 int vtkId = l.cells[i];
11218 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11219 if (!domain.count(anElem))
11221 int vtkType = grid->GetCellType(vtkId);
11222 int downId = grid->CellIdToDownId(vtkId);
11225 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11226 continue; // not OK at this stage of the algorithm:
11227 //no cells created after BuildDownWardConnectivity
11229 DownIdType aCell(downId, vtkType);
11230 cellDomains[aCell][idomain] = vtkId;
11231 celldom[vtkId] = idomain;
11232 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11238 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11239 // for each shared face, get the nodes
11240 // for each node, for each domain of the face, create a clone of the node
11242 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11243 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11244 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11246 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11247 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11248 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11250 //MESSAGE(".. Duplication of the nodes");
11251 for (int idomain = idom0; idomain < nbDomains; idomain++)
11253 itface = faceDomains.begin();
11254 for (; itface != faceDomains.end(); ++itface)
11256 const std::map<int, int>& domvol = itface->second;
11257 if (!domvol.count(idomain))
11259 DownIdType face = itface->first;
11260 //MESSAGE(" --- face " << face.cellId);
11261 std::set<int> oldNodes;
11263 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11264 std::set<int>::iterator itn = oldNodes.begin();
11265 for (; itn != oldNodes.end(); ++itn)
11268 if (nodeDomains[oldId].empty())
11270 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11271 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11273 std::map<int, int>::const_iterator itdom = domvol.begin();
11274 for (; itdom != domvol.end(); ++itdom)
11276 int idom = itdom->first;
11277 //MESSAGE(" domain " << idom);
11278 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11280 if (nodeDomains[oldId].size() >= 2) // a multiple node
11282 vector<int> orderedDoms;
11283 //MESSAGE("multiple node " << oldId);
11284 if (mutipleNodes.count(oldId))
11285 orderedDoms = mutipleNodes[oldId];
11288 map<int,int>::iterator it = nodeDomains[oldId].begin();
11289 for (; it != nodeDomains[oldId].end(); ++it)
11290 orderedDoms.push_back(it->first);
11292 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11293 //stringstream txt;
11294 //for (int i=0; i<orderedDoms.size(); i++)
11295 // txt << orderedDoms[i] << " ";
11296 //MESSAGE("orderedDoms " << txt.str());
11297 mutipleNodes[oldId] = orderedDoms;
11299 double *coords = grid->GetPoint(oldId);
11300 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11301 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11302 int newId = newNode->GetVtkID();
11303 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11304 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11311 //MESSAGE(".. Creation of elements");
11312 for (int idomain = idom0; idomain < nbDomains; idomain++)
11314 itface = faceDomains.begin();
11315 for (; itface != faceDomains.end(); ++itface)
11317 std::map<int, int> domvol = itface->second;
11318 if (!domvol.count(idomain))
11320 DownIdType face = itface->first;
11321 //MESSAGE(" --- face " << face.cellId);
11322 std::set<int> oldNodes;
11324 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11325 int nbMultipleNodes = 0;
11326 std::set<int>::iterator itn = oldNodes.begin();
11327 for (; itn != oldNodes.end(); ++itn)
11330 if (mutipleNodes.count(oldId))
11333 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11335 //MESSAGE("multiple Nodes detected on a shared face");
11336 int downId = itface->first.cellId;
11337 unsigned char cellType = itface->first.cellType;
11338 // --- shared edge or shared face ?
11339 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11342 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11343 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11344 if (mutipleNodes.count(nodes[i]))
11345 if (!mutipleNodesToFace.count(nodes[i]))
11346 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11348 else // shared face (between two volumes)
11350 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11351 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11352 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11353 for (int ie =0; ie < nbEdges; ie++)
11356 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11357 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11359 vector<int> vn0 = mutipleNodes[nodes[0]];
11360 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11362 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11363 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11364 if ( vn0[i0] == vn1[i1] )
11365 doms.push_back( vn0[ i0 ]);
11366 if ( doms.size() > 2 )
11368 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11369 double *coords = grid->GetPoint(nodes[0]);
11370 gp_Pnt p0(coords[0], coords[1], coords[2]);
11371 coords = grid->GetPoint(nodes[nbNodes - 1]);
11372 gp_Pnt p1(coords[0], coords[1], coords[2]);
11374 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11375 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11376 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11377 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11378 for ( size_t id = 0; id < doms.size(); id++ )
11380 int idom = doms[id];
11381 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11382 for ( int ivol = 0; ivol < nbvol; ivol++ )
11384 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11385 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11386 if (domain.count(elem))
11388 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11389 domvol[idom] = (SMDS_MeshVolume*) svol;
11390 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11391 double values[3] = { 0,0,0 };
11392 vtkIdType npts = 0;
11393 vtkIdType const *pts(nullptr);
11394 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11395 for ( vtkIdType i = 0; i < npts; ++i )
11397 double *coords = grid->GetPoint( pts[i] );
11398 for ( int j = 0; j < 3; ++j )
11399 values[j] += coords[j] / npts;
11403 gref.SetCoord( values[0], values[1], values[2] );
11404 angleDom[idom] = 0;
11408 gp_Pnt g( values[0], values[1], values[2] );
11409 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11410 //MESSAGE(" angle=" << angleDom[idom]);
11416 map<double, int> sortedDom; // sort domains by angle
11417 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11418 sortedDom[ia->second] = ia->first;
11419 vector<int> vnodes;
11421 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11423 vdom.push_back(ib->second);
11424 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11426 for (int ino = 0; ino < nbNodes; ino++)
11427 vnodes.push_back(nodes[ino]);
11428 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11437 // --- iterate on shared faces (volumes to modify, face to extrude)
11438 // get node id's of the face (id SMDS = id VTK)
11439 // create flat element with old and new nodes if requested
11441 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11442 // (domain1 X domain2) = domain1 + MAXINT*domain2
11444 std::map<int, std::map<long,int> > nodeQuadDomains;
11445 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11447 //MESSAGE(".. Creation of elements: simple junction");
11448 if (createJointElems)
11450 string joints2DName = "joints2D";
11451 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11452 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11453 string joints3DName = "joints3D";
11454 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11455 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11457 itface = faceDomains.begin();
11458 for (; itface != faceDomains.end(); ++itface)
11460 DownIdType face = itface->first;
11461 std::set<int> oldNodes;
11462 std::set<int>::iterator itn;
11464 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11466 std::map<int, int> domvol = itface->second;
11467 std::map<int, int>::iterator itdom = domvol.begin();
11468 int dom1 = itdom->first;
11469 int vtkVolId = itdom->second;
11471 int dom2 = itdom->first;
11472 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11474 stringstream grpname;
11477 grpname << dom1 << "_" << dom2;
11479 grpname << dom2 << "_" << dom1;
11480 string namegrp = grpname.str();
11481 if (!mapOfJunctionGroups.count(namegrp))
11482 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11483 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11485 sgrp->Add(vol->GetID());
11486 if (vol->GetType() == SMDSAbs_Volume)
11487 joints3DGrp->Add(vol->GetID());
11488 else if (vol->GetType() == SMDSAbs_Face)
11489 joints2DGrp->Add(vol->GetID());
11493 // --- create volumes on multiple domain intersection if requested
11494 // iterate on mutipleNodesToFace
11495 // iterate on edgesMultiDomains
11497 //MESSAGE(".. Creation of elements: multiple junction");
11498 if (createJointElems)
11500 // --- iterate on mutipleNodesToFace
11502 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11503 for (; itn != mutipleNodesToFace.end(); ++itn)
11505 int node = itn->first;
11506 vector<int> orderDom = itn->second;
11507 vector<vtkIdType> orderedNodes;
11508 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11509 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11510 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11512 stringstream grpname;
11514 grpname << 0 << "_" << 0;
11515 string namegrp = grpname.str();
11516 if (!mapOfJunctionGroups.count(namegrp))
11517 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11518 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11520 sgrp->Add(face->GetID());
11523 // --- iterate on edgesMultiDomains
11525 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11526 for (; ite != edgesMultiDomains.end(); ++ite)
11528 vector<int> nodes = ite->first;
11529 vector<int> orderDom = ite->second;
11530 vector<vtkIdType> orderedNodes;
11531 if (nodes.size() == 2)
11533 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11534 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11535 if ( orderDom.size() == 3 )
11536 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11537 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11539 for (int idom = orderDom.size()-1; idom >=0; idom--)
11540 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11541 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11543 string namegrp = "jointsMultiples";
11544 if (!mapOfJunctionGroups.count(namegrp))
11545 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11546 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11548 sgrp->Add(vol->GetID());
11552 //INFOS("Quadratic multiple joints not implemented");
11553 // TODO quadratic nodes
11558 // --- list the explicit faces and edges of the mesh that need to be modified,
11559 // i.e. faces and edges built with one or more duplicated nodes.
11560 // associate these faces or edges to their corresponding domain.
11561 // only the first domain found is kept when a face or edge is shared
11563 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11564 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11565 faceOrEdgeDom.clear();
11568 //MESSAGE(".. Modification of elements");
11569 for (int idomain = idom0; idomain < nbDomains; idomain++)
11571 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11572 for (; itnod != nodeDomains.end(); ++itnod)
11574 int oldId = itnod->first;
11575 //MESSAGE(" node " << oldId);
11576 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11577 for (int i = 0; i < l.ncells; i++)
11579 int vtkId = l.cells[i];
11580 int vtkType = grid->GetCellType(vtkId);
11581 int downId = grid->CellIdToDownId(vtkId);
11583 continue; // new cells: not to be modified
11584 DownIdType aCell(downId, vtkType);
11585 int volParents[1000];
11586 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11587 for (int j = 0; j < nbvol; j++)
11588 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11589 if (!feDom.count(vtkId))
11591 feDom[vtkId] = idomain;
11592 faceOrEdgeDom[aCell] = emptyMap;
11593 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11594 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11595 // << " type " << vtkType << " downId " << downId);
11601 // --- iterate on shared faces (volumes to modify, face to extrude)
11602 // get node id's of the face
11603 // replace old nodes by new nodes in volumes, and update inverse connectivity
11605 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11606 for (int m=0; m<3; m++)
11608 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11609 itface = (*amap).begin();
11610 for (; itface != (*amap).end(); ++itface)
11612 DownIdType face = itface->first;
11613 std::set<int> oldNodes;
11614 std::set<int>::iterator itn;
11616 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11617 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11618 std::map<int, int> localClonedNodeIds;
11620 std::map<int, int> domvol = itface->second;
11621 std::map<int, int>::iterator itdom = domvol.begin();
11622 for (; itdom != domvol.end(); ++itdom)
11624 int idom = itdom->first;
11625 int vtkVolId = itdom->second;
11626 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11627 localClonedNodeIds.clear();
11628 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11631 if (nodeDomains[oldId].count(idom))
11633 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11634 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11637 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11642 // Remove empty groups (issue 0022812)
11643 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11644 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11646 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11647 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11650 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11651 grid->DeleteLinks();
11659 * \brief Double nodes on some external faces and create flat elements.
11660 * Flat elements are mainly used by some types of mechanic calculations.
11662 * Each group of the list must be constituted of faces.
11663 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11664 * @param theElems - list of groups of faces, where a group of faces is a set of
11665 * SMDS_MeshElements sorted by Id.
11666 * @return TRUE if operation has been completed successfully, FALSE otherwise
11668 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11670 // MESSAGE("-------------------------------------------------");
11671 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11672 // MESSAGE("-------------------------------------------------");
11674 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11676 // --- For each group of faces
11677 // duplicate the nodes, create a flat element based on the face
11678 // replace the nodes of the faces by their clones
11680 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11681 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11682 clonedNodes.clear();
11683 intermediateNodes.clear();
11684 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11685 mapOfJunctionGroups.clear();
11687 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11689 const TIDSortedElemSet& domain = theElems[idom];
11690 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11691 for ( ; elemItr != domain.end(); ++elemItr )
11693 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11696 // MESSAGE("aFace=" << aFace->GetID());
11697 bool isQuad = aFace->IsQuadratic();
11698 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11700 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11702 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11703 while (nodeIt->more())
11705 const SMDS_MeshNode* node = nodeIt->next();
11706 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11708 ln2.push_back(node);
11710 ln0.push_back(node);
11712 const SMDS_MeshNode* clone = 0;
11713 if (!clonedNodes.count(node))
11715 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11716 copyPosition( node, clone );
11717 clonedNodes[node] = clone;
11720 clone = clonedNodes[node];
11723 ln3.push_back(clone);
11725 ln1.push_back(clone);
11727 const SMDS_MeshNode* inter = 0;
11728 if (isQuad && (!isMedium))
11730 if (!intermediateNodes.count(node))
11732 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11733 copyPosition( node, inter );
11734 intermediateNodes[node] = inter;
11737 inter = intermediateNodes[node];
11738 ln4.push_back(inter);
11742 // --- extrude the face
11744 vector<const SMDS_MeshNode*> ln;
11745 SMDS_MeshVolume* vol = 0;
11746 vtkIdType aType = aFace->GetVtkType();
11750 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11751 // MESSAGE("vol prism " << vol->GetID());
11752 ln.push_back(ln1[0]);
11753 ln.push_back(ln1[1]);
11754 ln.push_back(ln1[2]);
11757 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11758 // MESSAGE("vol hexa " << vol->GetID());
11759 ln.push_back(ln1[0]);
11760 ln.push_back(ln1[1]);
11761 ln.push_back(ln1[2]);
11762 ln.push_back(ln1[3]);
11764 case VTK_QUADRATIC_TRIANGLE:
11765 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11766 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11767 // MESSAGE("vol quad prism " << vol->GetID());
11768 ln.push_back(ln1[0]);
11769 ln.push_back(ln1[1]);
11770 ln.push_back(ln1[2]);
11771 ln.push_back(ln3[0]);
11772 ln.push_back(ln3[1]);
11773 ln.push_back(ln3[2]);
11775 case VTK_QUADRATIC_QUAD:
11776 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11777 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11778 // ln4[0], ln4[1], ln4[2], ln4[3]);
11779 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11780 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11781 ln4[0], ln4[1], ln4[2], ln4[3]);
11782 // MESSAGE("vol quad hexa " << vol->GetID());
11783 ln.push_back(ln1[0]);
11784 ln.push_back(ln1[1]);
11785 ln.push_back(ln1[2]);
11786 ln.push_back(ln1[3]);
11787 ln.push_back(ln3[0]);
11788 ln.push_back(ln3[1]);
11789 ln.push_back(ln3[2]);
11790 ln.push_back(ln3[3]);
11800 stringstream grpname;
11803 string namegrp = grpname.str();
11804 if (!mapOfJunctionGroups.count(namegrp))
11805 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11806 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11808 sgrp->Add(vol->GetID());
11811 // --- modify the face
11813 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11820 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11821 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11822 * groups of faces to remove inside the object, (idem edges).
11823 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11825 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11826 const TopoDS_Shape& theShape,
11827 SMESH_NodeSearcher* theNodeSearcher,
11828 const char* groupName,
11829 std::vector<double>& nodesCoords,
11830 std::vector<std::vector<int> >& listOfListOfNodes)
11832 // MESSAGE("--------------------------------");
11833 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11834 // MESSAGE("--------------------------------");
11836 // --- zone of volumes to remove is given :
11837 // 1 either by a geom shape (one or more vertices) and a radius,
11838 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11839 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11840 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11841 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11842 // defined by it's name.
11844 SMESHDS_GroupBase* groupDS = 0;
11845 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11846 while ( groupIt->more() )
11849 SMESH_Group * group = groupIt->next();
11850 if ( !group ) continue;
11851 groupDS = group->GetGroupDS();
11852 if ( !groupDS || groupDS->IsEmpty() ) continue;
11853 std::string grpName = group->GetName();
11854 //MESSAGE("grpName=" << grpName);
11855 if (grpName == groupName)
11861 bool isNodeGroup = false;
11862 bool isNodeCoords = false;
11865 if (groupDS->GetType() != SMDSAbs_Node)
11867 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11870 if (nodesCoords.size() > 0)
11871 isNodeCoords = true; // a list o nodes given by their coordinates
11872 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11874 // --- define groups to build
11876 // --- group of SMDS volumes
11877 string grpvName = groupName;
11878 grpvName += "_vol";
11879 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11882 MESSAGE("group not created " << grpvName);
11885 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11887 // --- group of SMDS faces on the skin
11888 string grpsName = groupName;
11889 grpsName += "_skin";
11890 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11893 MESSAGE("group not created " << grpsName);
11896 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11898 // --- group of SMDS faces internal (several shapes)
11899 string grpiName = groupName;
11900 grpiName += "_internalFaces";
11901 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11904 MESSAGE("group not created " << grpiName);
11907 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11909 // --- group of SMDS faces internal (several shapes)
11910 string grpeiName = groupName;
11911 grpeiName += "_internalEdges";
11912 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11915 MESSAGE("group not created " << grpeiName);
11918 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11920 // --- build downward connectivity
11922 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11923 meshDS->BuildDownWardConnectivity(true);
11924 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11926 // --- set of volumes detected inside
11928 std::set<int> setOfInsideVol;
11929 std::set<int> setOfVolToCheck;
11931 std::vector<gp_Pnt> gpnts;
11934 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11936 //MESSAGE("group of nodes provided");
11937 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11938 while ( elemIt->more() )
11940 const SMDS_MeshElement* elem = elemIt->next();
11943 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11946 SMDS_MeshElement* vol = 0;
11947 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11948 while (volItr->more())
11950 vol = (SMDS_MeshElement*)volItr->next();
11951 setOfInsideVol.insert(vol->GetVtkID());
11952 sgrp->Add(vol->GetID());
11956 else if (isNodeCoords)
11958 //MESSAGE("list of nodes coordinates provided");
11961 while ( i < nodesCoords.size()-2 )
11963 double x = nodesCoords[i++];
11964 double y = nodesCoords[i++];
11965 double z = nodesCoords[i++];
11966 gp_Pnt p = gp_Pnt(x, y ,z);
11967 gpnts.push_back(p);
11968 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11972 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11974 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11975 TopTools_IndexedMapOfShape vertexMap;
11976 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11977 gp_Pnt p = gp_Pnt(0,0,0);
11978 if (vertexMap.Extent() < 1)
11981 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11983 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11984 p = BRep_Tool::Pnt(vertex);
11985 gpnts.push_back(p);
11986 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11990 if (gpnts.size() > 0)
11992 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11993 //MESSAGE("startNode->nodeId " << nodeId);
11995 double radius2 = radius*radius;
11996 //MESSAGE("radius2 " << radius2);
11998 // --- volumes on start node
12000 setOfVolToCheck.clear();
12001 SMDS_MeshElement* startVol = 0;
12002 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12003 while (volItr->more())
12005 startVol = (SMDS_MeshElement*)volItr->next();
12006 setOfVolToCheck.insert(startVol->GetVtkID());
12008 if (setOfVolToCheck.empty())
12010 MESSAGE("No volumes found");
12014 // --- starting with central volumes then their neighbors, check if they are inside
12015 // or outside the domain, until no more new neighbor volume is inside.
12016 // Fill the group of inside volumes
12018 std::map<int, double> mapOfNodeDistance2;
12019 mapOfNodeDistance2.clear();
12020 std::set<int> setOfOutsideVol;
12021 while (!setOfVolToCheck.empty())
12023 std::set<int>::iterator it = setOfVolToCheck.begin();
12025 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12026 bool volInside = false;
12027 vtkIdType npts = 0;
12028 vtkIdType const *pts(nullptr);
12029 grid->GetCellPoints(vtkId, npts, pts);
12030 for (int i=0; i<npts; i++)
12032 double distance2 = 0;
12033 if (mapOfNodeDistance2.count(pts[i]))
12035 distance2 = mapOfNodeDistance2[pts[i]];
12036 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12040 double *coords = grid->GetPoint(pts[i]);
12041 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12043 for ( size_t j = 0; j < gpnts.size(); j++ )
12045 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12046 if (d2 < distance2)
12049 if (distance2 < radius2)
12053 mapOfNodeDistance2[pts[i]] = distance2;
12054 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12056 if (distance2 < radius2)
12058 volInside = true; // one or more nodes inside the domain
12059 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12065 setOfInsideVol.insert(vtkId);
12066 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12067 int neighborsVtkIds[NBMAXNEIGHBORS];
12068 int downIds[NBMAXNEIGHBORS];
12069 unsigned char downTypes[NBMAXNEIGHBORS];
12070 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12071 for (int n = 0; n < nbNeighbors; n++)
12072 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12073 setOfVolToCheck.insert(neighborsVtkIds[n]);
12077 setOfOutsideVol.insert(vtkId);
12078 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12080 setOfVolToCheck.erase(vtkId);
12084 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12085 // If yes, add the volume to the inside set
12087 bool addedInside = true;
12088 std::set<int> setOfVolToReCheck;
12089 while (addedInside)
12091 //MESSAGE(" --------------------------- re check");
12092 addedInside = false;
12093 std::set<int>::iterator itv = setOfInsideVol.begin();
12094 for (; itv != setOfInsideVol.end(); ++itv)
12097 int neighborsVtkIds[NBMAXNEIGHBORS];
12098 int downIds[NBMAXNEIGHBORS];
12099 unsigned char downTypes[NBMAXNEIGHBORS];
12100 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12101 for (int n = 0; n < nbNeighbors; n++)
12102 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12103 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12105 setOfVolToCheck = setOfVolToReCheck;
12106 setOfVolToReCheck.clear();
12107 while (!setOfVolToCheck.empty())
12109 std::set<int>::iterator it = setOfVolToCheck.begin();
12111 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12113 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12114 int countInside = 0;
12115 int neighborsVtkIds[NBMAXNEIGHBORS];
12116 int downIds[NBMAXNEIGHBORS];
12117 unsigned char downTypes[NBMAXNEIGHBORS];
12118 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119 for (int n = 0; n < nbNeighbors; n++)
12120 if (setOfInsideVol.count(neighborsVtkIds[n]))
12122 //MESSAGE("countInside " << countInside);
12123 if (countInside > 1)
12125 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12126 setOfInsideVol.insert(vtkId);
12127 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12128 addedInside = true;
12131 setOfVolToReCheck.insert(vtkId);
12133 setOfVolToCheck.erase(vtkId);
12137 // --- map of Downward faces at the boundary, inside the global volume
12138 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12139 // fill group of SMDS faces inside the volume (when several volume shapes)
12140 // fill group of SMDS faces on the skin of the global volume (if skin)
12142 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12143 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12144 std::set<int>::iterator it = setOfInsideVol.begin();
12145 for (; it != setOfInsideVol.end(); ++it)
12148 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12149 int neighborsVtkIds[NBMAXNEIGHBORS];
12150 int downIds[NBMAXNEIGHBORS];
12151 unsigned char downTypes[NBMAXNEIGHBORS];
12152 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12153 for (int n = 0; n < nbNeighbors; n++)
12155 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12156 if (neighborDim == 3)
12158 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12160 DownIdType face(downIds[n], downTypes[n]);
12161 boundaryFaces[face] = vtkId;
12163 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12164 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12165 if (vtkFaceId >= 0)
12167 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12168 // find also the smds edges on this face
12169 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12170 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12171 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12172 for (int i = 0; i < nbEdges; i++)
12174 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12175 if (vtkEdgeId >= 0)
12176 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12180 else if (neighborDim == 2) // skin of the volume
12182 DownIdType face(downIds[n], downTypes[n]);
12183 skinFaces[face] = vtkId;
12184 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12185 if (vtkFaceId >= 0)
12186 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12191 // --- identify the edges constituting the wire of each subshape on the skin
12192 // define polylines with the nodes of edges, equivalent to wires
12193 // project polylines on subshapes, and partition, to get geom faces
12195 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12196 std::set<int> emptySet;
12198 std::set<int> shapeIds;
12200 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12201 while (itelem->more())
12203 const SMDS_MeshElement *elem = itelem->next();
12204 int shapeId = elem->getshapeId();
12205 int vtkId = elem->GetVtkID();
12206 if (!shapeIdToVtkIdSet.count(shapeId))
12208 shapeIdToVtkIdSet[shapeId] = emptySet;
12209 shapeIds.insert(shapeId);
12211 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12214 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12215 std::set<DownIdType, DownIdCompare> emptyEdges;
12216 emptyEdges.clear();
12218 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12219 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12221 int shapeId = itShape->first;
12222 //MESSAGE(" --- Shape ID --- "<< shapeId);
12223 shapeIdToEdges[shapeId] = emptyEdges;
12225 std::vector<int> nodesEdges;
12227 std::set<int>::iterator its = itShape->second.begin();
12228 for (; its != itShape->second.end(); ++its)
12231 //MESSAGE(" " << vtkId);
12232 int neighborsVtkIds[NBMAXNEIGHBORS];
12233 int downIds[NBMAXNEIGHBORS];
12234 unsigned char downTypes[NBMAXNEIGHBORS];
12235 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12236 for (int n = 0; n < nbNeighbors; n++)
12238 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12240 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12241 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12242 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12244 DownIdType edge(downIds[n], downTypes[n]);
12245 if (!shapeIdToEdges[shapeId].count(edge))
12247 shapeIdToEdges[shapeId].insert(edge);
12249 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12250 nodesEdges.push_back(vtkNodeId[0]);
12251 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12252 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12258 std::list<int> order;
12260 if (nodesEdges.size() > 0)
12262 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12263 nodesEdges[0] = -1;
12264 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12265 nodesEdges[1] = -1; // do not reuse this edge
12269 int nodeTofind = order.back(); // try first to push back
12271 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12272 if (nodesEdges[i] == nodeTofind)
12274 if ( i == (int) nodesEdges.size() )
12275 found = false; // no follower found on back
12278 if (i%2) // odd ==> use the previous one
12279 if (nodesEdges[i-1] < 0)
12283 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12284 nodesEdges[i-1] = -1;
12286 else // even ==> use the next one
12287 if (nodesEdges[i+1] < 0)
12291 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12292 nodesEdges[i+1] = -1;
12297 // try to push front
12299 nodeTofind = order.front(); // try to push front
12300 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12301 if ( nodesEdges[i] == nodeTofind )
12303 if ( i == (int)nodesEdges.size() )
12305 found = false; // no predecessor found on front
12308 if (i%2) // odd ==> use the previous one
12309 if (nodesEdges[i-1] < 0)
12313 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12314 nodesEdges[i-1] = -1;
12316 else // even ==> use the next one
12317 if (nodesEdges[i+1] < 0)
12321 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12322 nodesEdges[i+1] = -1;
12328 std::vector<int> nodes;
12329 nodes.push_back(shapeId);
12330 std::list<int>::iterator itl = order.begin();
12331 for (; itl != order.end(); itl++)
12333 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12334 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12336 listOfListOfNodes.push_back(nodes);
12339 // partition geom faces with blocFissure
12340 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12341 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12347 //================================================================================
12349 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12350 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12351 * \return TRUE if operation has been completed successfully, FALSE otherwise
12353 //================================================================================
12355 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12357 // iterates on volume elements and detect all free faces on them
12358 SMESHDS_Mesh* aMesh = GetMeshDS();
12362 ElemFeatures faceType( SMDSAbs_Face );
12363 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12364 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12367 const SMDS_MeshVolume* volume = vIt->next();
12368 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12369 vTool.SetExternalNormal();
12370 const int iQuad = volume->IsQuadratic();
12371 faceType.SetQuad( iQuad );
12372 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12374 if (!vTool.IsFreeFace(iface))
12377 vector<const SMDS_MeshNode *> nodes;
12378 int nbFaceNodes = vTool.NbFaceNodes(iface);
12379 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12381 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12382 nodes.push_back(faceNodes[inode]);
12384 if (iQuad) // add medium nodes
12386 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12387 nodes.push_back(faceNodes[inode]);
12388 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12389 nodes.push_back(faceNodes[8]);
12391 // add new face based on volume nodes
12392 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12394 nbExisted++; // face already exists
12398 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12403 return ( nbFree == ( nbExisted + nbCreated ));
12408 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12410 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12412 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12415 //================================================================================
12417 * \brief Creates missing boundary elements
12418 * \param elements - elements whose boundary is to be checked
12419 * \param dimension - defines type of boundary elements to create
12420 * \param group - a group to store created boundary elements in
12421 * \param targetMesh - a mesh to store created boundary elements in
12422 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12423 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12424 * boundary elements will be copied into the targetMesh
12425 * \param toAddExistingBondary - if true, not only new but also pre-existing
12426 * boundary elements will be added into the new group
12427 * \param aroundElements - if true, elements will be created on boundary of given
12428 * elements else, on boundary of the whole mesh.
12429 * \return nb of added boundary elements
12431 //================================================================================
12433 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12434 Bnd_Dimension dimension,
12435 SMESH_Group* group/*=0*/,
12436 SMESH_Mesh* targetMesh/*=0*/,
12437 bool toCopyElements/*=false*/,
12438 bool toCopyExistingBoundary/*=false*/,
12439 bool toAddExistingBondary/*= false*/,
12440 bool aroundElements/*= false*/)
12442 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12443 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12444 // hope that all elements are of the same type, do not check them all
12445 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12446 throw SALOME_Exception(LOCALIZED("wrong element type"));
12449 toCopyElements = toCopyExistingBoundary = false;
12451 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12452 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12453 int nbAddedBnd = 0;
12455 // editor adding present bnd elements and optionally holding elements to add to the group
12456 SMESH_MeshEditor* presentEditor;
12457 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12458 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12460 SMESH_MesherHelper helper( *myMesh );
12461 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12462 SMDS_VolumeTool vTool;
12463 TIDSortedElemSet avoidSet;
12464 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12467 typedef vector<const SMDS_MeshNode*> TConnectivity;
12468 TConnectivity tgtNodes;
12469 ElemFeatures elemKind( missType ), elemToCopy;
12471 vector<const SMDS_MeshElement*> presentBndElems;
12472 vector<TConnectivity> missingBndElems;
12473 vector<int> freeFacets;
12474 TConnectivity nodes, elemNodes;
12476 SMDS_ElemIteratorPtr eIt;
12477 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12478 else eIt = SMESHUtils::elemSetIterator( elements );
12480 while ( eIt->more() )
12482 const SMDS_MeshElement* elem = eIt->next();
12483 const int iQuad = elem->IsQuadratic();
12484 elemKind.SetQuad( iQuad );
12486 // ------------------------------------------------------------------------------------
12487 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12488 // ------------------------------------------------------------------------------------
12489 presentBndElems.clear();
12490 missingBndElems.clear();
12491 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12492 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12494 const SMDS_MeshElement* otherVol = 0;
12495 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12497 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12498 ( !aroundElements || elements.count( otherVol )))
12500 freeFacets.push_back( iface );
12502 if ( missType == SMDSAbs_Face )
12503 vTool.SetExternalNormal();
12504 for ( size_t i = 0; i < freeFacets.size(); ++i )
12506 int iface = freeFacets[i];
12507 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12508 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12509 if ( missType == SMDSAbs_Edge ) // boundary edges
12511 nodes.resize( 2+iQuad );
12512 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12514 for ( size_t j = 0; j < nodes.size(); ++j )
12515 nodes[ j ] = nn[ i+j ];
12516 if ( const SMDS_MeshElement* edge =
12517 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12518 presentBndElems.push_back( edge );
12520 missingBndElems.push_back( nodes );
12523 else // boundary face
12526 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12527 nodes.push_back( nn[inode] ); // add corner nodes
12529 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12530 nodes.push_back( nn[inode] ); // add medium nodes
12531 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12533 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12535 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12536 SMDSAbs_Face, /*noMedium=*/false ))
12537 presentBndElems.push_back( f );
12539 missingBndElems.push_back( nodes );
12541 if ( targetMesh != myMesh )
12543 // add 1D elements on face boundary to be added to a new mesh
12544 const SMDS_MeshElement* edge;
12545 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12548 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12550 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12551 if ( edge && avoidSet.insert( edge ).second )
12552 presentBndElems.push_back( edge );
12558 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12560 avoidSet.clear(), avoidSet.insert( elem );
12561 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12562 SMDS_MeshElement::iterator() );
12563 elemNodes.push_back( elemNodes[0] );
12564 nodes.resize( 2 + iQuad );
12565 const int nbLinks = elem->NbCornerNodes();
12566 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12568 nodes[0] = elemNodes[iN];
12569 nodes[1] = elemNodes[iN+1+iQuad];
12570 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12571 continue; // not free link
12573 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12574 if ( const SMDS_MeshElement* edge =
12575 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12576 presentBndElems.push_back( edge );
12578 missingBndElems.push_back( nodes );
12582 // ---------------------------------
12583 // 2. Add missing boundary elements
12584 // ---------------------------------
12585 if ( targetMesh != myMesh )
12586 // instead of making a map of nodes in this mesh and targetMesh,
12587 // we create nodes with same IDs.
12588 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12590 TConnectivity& srcNodes = missingBndElems[i];
12591 tgtNodes.resize( srcNodes.size() );
12592 for ( inode = 0; inode < srcNodes.size(); ++inode )
12593 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12594 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12596 /*noMedium=*/false))
12598 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12602 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12604 TConnectivity& nodes = missingBndElems[ i ];
12605 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12607 /*noMedium=*/false))
12609 SMDS_MeshElement* newElem =
12610 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12611 nbAddedBnd += bool( newElem );
12613 // try to set a new element to a shape
12614 if ( myMesh->HasShapeToMesh() )
12617 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12618 const size_t nbN = nodes.size() / (iQuad+1 );
12619 for ( inode = 0; inode < nbN && ok; ++inode )
12621 pair<int, TopAbs_ShapeEnum> i_stype =
12622 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12623 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12624 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12626 if ( ok && mediumShapes.size() > 1 )
12628 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12629 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12630 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12632 if (( ok = ( stype_i->first != stype_i_0.first )))
12633 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12634 aMesh->IndexToShape( stype_i_0.second ));
12637 if ( ok && mediumShapes.begin()->first == missShapeType )
12638 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12642 // ----------------------------------
12643 // 3. Copy present boundary elements
12644 // ----------------------------------
12645 if ( toCopyExistingBoundary )
12646 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12648 const SMDS_MeshElement* e = presentBndElems[i];
12649 tgtNodes.resize( e->NbNodes() );
12650 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12651 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12652 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12654 else // store present elements to add them to a group
12655 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12657 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12660 } // loop on given elements
12662 // ---------------------------------------------
12663 // 4. Fill group with boundary elements
12664 // ---------------------------------------------
12667 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12668 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12669 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12671 tgtEditor.myLastCreatedElems.clear();
12672 tgtEditor2.myLastCreatedElems.clear();
12674 // -----------------------
12675 // 5. Copy given elements
12676 // -----------------------
12677 if ( toCopyElements && targetMesh != myMesh )
12679 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12680 else eIt = SMESHUtils::elemSetIterator( elements );
12681 while (eIt->more())
12683 const SMDS_MeshElement* elem = eIt->next();
12684 tgtNodes.resize( elem->NbNodes() );
12685 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12686 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12687 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12689 tgtEditor.myLastCreatedElems.clear();
12695 //================================================================================
12697 * \brief Copy node position and set \a to node on the same geometry
12699 //================================================================================
12701 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12702 const SMDS_MeshNode* to )
12704 if ( !from || !to ) return;
12706 SMDS_PositionPtr pos = from->GetPosition();
12707 if ( !pos || from->getshapeId() < 1 ) return;
12709 switch ( pos->GetTypeOfPosition() )
12711 case SMDS_TOP_3DSPACE: break;
12713 case SMDS_TOP_FACE:
12715 SMDS_FacePositionPtr fPos = pos;
12716 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12717 fPos->GetUParameter(), fPos->GetVParameter() );
12720 case SMDS_TOP_EDGE:
12722 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12723 SMDS_EdgePositionPtr ePos = pos;
12724 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12727 case SMDS_TOP_VERTEX:
12729 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12732 case SMDS_TOP_UNSPEC: