1 // Copyright (C) 2007-2021 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 TSplitMethod(const TSplitMethod &splitMethod)
1812 : _nbSplits(splitMethod._nbSplits),
1813 _nbCorners(splitMethod._nbCorners),
1814 _baryNode(splitMethod._baryNode),
1815 _ownConn(splitMethod._ownConn),
1816 _faceBaryNode(splitMethod._faceBaryNode)
1818 _connectivity = splitMethod._connectivity;
1819 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1820 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1822 bool hasFacet( const TTriangleFacet& facet ) const
1824 if ( _nbCorners == 4 )
1826 const int* tetConn = _connectivity;
1827 for ( ; tetConn[0] >= 0; tetConn += 4 )
1828 if (( facet.contains( tetConn[0] ) +
1829 facet.contains( tetConn[1] ) +
1830 facet.contains( tetConn[2] ) +
1831 facet.contains( tetConn[3] )) == 3 )
1834 else // prism, _nbCorners == 6
1836 const int* prismConn = _connectivity;
1837 for ( ; prismConn[0] >= 0; prismConn += 6 )
1839 if (( facet.contains( prismConn[0] ) &&
1840 facet.contains( prismConn[1] ) &&
1841 facet.contains( prismConn[2] ))
1843 ( facet.contains( prismConn[3] ) &&
1844 facet.contains( prismConn[4] ) &&
1845 facet.contains( prismConn[5] )))
1853 //=======================================================================
1855 * \brief return TSplitMethod for the given element to split into tetrahedra
1857 //=======================================================================
1859 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1861 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1863 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1864 // an edge and a face barycenter; tertaherdons are based on triangles and
1865 // a volume barycenter
1866 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1868 // Find out how adjacent volumes are split
1870 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1871 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1872 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1874 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1875 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1876 if ( nbNodes < 4 ) continue;
1878 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1879 const int* nInd = vol.GetFaceNodesIndices( iF );
1882 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1883 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1884 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1885 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1889 int iCom = 0; // common node of triangle faces to split into
1890 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1892 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1893 nInd[ iQ * ( (iCom+1)%nbNodes )],
1894 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1895 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1896 nInd[ iQ * ( (iCom+2)%nbNodes )],
1897 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1898 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1900 triaSplits.push_back( t012 );
1901 triaSplits.push_back( t023 );
1906 if ( !triaSplits.empty() )
1907 hasAdjacentSplits = true;
1910 // Among variants of split method select one compliant with adjacent volumes
1912 TSplitMethod method;
1913 if ( !vol.Element()->IsPoly() && !is24TetMode )
1915 int nbVariants = 2, nbTet = 0;
1916 const int** connVariants = 0;
1917 switch ( vol.Element()->GetEntityType() )
1919 case SMDSEntity_Hexa:
1920 case SMDSEntity_Quad_Hexa:
1921 case SMDSEntity_TriQuad_Hexa:
1922 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1923 connVariants = theHexTo5, nbTet = 5;
1925 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1927 case SMDSEntity_Pyramid:
1928 case SMDSEntity_Quad_Pyramid:
1929 connVariants = thePyraTo2; nbTet = 2;
1931 case SMDSEntity_Penta:
1932 case SMDSEntity_Quad_Penta:
1933 case SMDSEntity_BiQuad_Penta:
1934 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1939 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1941 // check method compliance with adjacent tetras,
1942 // all found splits must be among facets of tetras described by this method
1943 method = TSplitMethod( nbTet, connVariants[variant] );
1944 if ( hasAdjacentSplits && method._nbSplits > 0 )
1946 bool facetCreated = true;
1947 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1949 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1950 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1951 facetCreated = method.hasFacet( *facet );
1953 if ( !facetCreated )
1954 method = TSplitMethod(0); // incompatible method
1958 if ( method._nbSplits < 1 )
1960 // No standard method is applicable, use a generic solution:
1961 // each facet of a volume is split into triangles and
1962 // each of triangles and a volume barycenter form a tetrahedron.
1964 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1966 int* connectivity = new int[ maxTetConnSize + 1 ];
1967 method._connectivity = connectivity;
1968 method._ownConn = true;
1969 method._baryNode = !isHex27; // to create central node or not
1972 int baryCenInd = vol.NbNodes() - int( isHex27 );
1973 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1975 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1976 const int* nInd = vol.GetFaceNodesIndices( iF );
1977 // find common node of triangle facets of tetra to create
1978 int iCommon = 0; // index in linear numeration
1979 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1980 if ( !triaSplits.empty() )
1983 const TTriangleFacet* facet = &triaSplits.front();
1984 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1985 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1986 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1989 else if ( nbNodes > 3 && !is24TetMode )
1991 // find the best method of splitting into triangles by aspect ratio
1992 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1993 map< double, int > badness2iCommon;
1994 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1995 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1996 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1999 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2001 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2002 nodes[ iQ*((iLast-1)%nbNodes)],
2003 nodes[ iQ*((iLast )%nbNodes)]);
2004 badness += getBadRate( &tria, aspectRatio );
2006 badness2iCommon.insert( make_pair( badness, iCommon ));
2008 // use iCommon with lowest badness
2009 iCommon = badness2iCommon.begin()->second;
2011 if ( iCommon >= nbNodes )
2012 iCommon = 0; // something wrong
2014 // fill connectivity of tetrahedra based on a current face
2015 int nbTet = nbNodes - 2;
2016 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2021 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2022 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2026 method._faceBaryNode[ iF ] = 0;
2027 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2030 for ( int i = 0; i < nbTet; ++i )
2032 int i1 = i, i2 = (i+1) % nbNodes;
2033 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2034 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2035 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2036 connectivity[ connSize++ ] = faceBaryCenInd;
2037 connectivity[ connSize++ ] = baryCenInd;
2042 for ( int i = 0; i < nbTet; ++i )
2044 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2045 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2046 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2047 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2048 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2049 connectivity[ connSize++ ] = baryCenInd;
2052 method._nbSplits += nbTet;
2054 } // loop on volume faces
2056 connectivity[ connSize++ ] = -1;
2058 } // end of generic solution
2062 //=======================================================================
2064 * \brief return TSplitMethod to split haxhedron into prisms
2066 //=======================================================================
2068 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2069 const int methodFlags,
2070 const int facetToSplit)
2072 TSplitMethod method;
2074 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2076 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2078 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2080 static TSplitMethod to4methods[4]; // order BT, LR, FB
2081 if ( to4methods[iF]._nbSplits == 0 )
2085 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2086 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2087 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2090 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2091 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2092 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2095 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2096 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2097 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2099 default: return to4methods[3];
2101 to4methods[iF]._nbSplits = 4;
2102 to4methods[iF]._nbCorners = 6;
2104 method = to4methods[iF];
2105 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2108 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2110 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2112 const int nbVariants = 2, nbSplits = 2;
2113 const int** connVariants = 0;
2115 case 0: connVariants = theHexTo2Prisms_BT; break;
2116 case 1: connVariants = theHexTo2Prisms_LR; break;
2117 case 2: connVariants = theHexTo2Prisms_FB; break;
2118 default: return method;
2121 // look for prisms adjacent via facetToSplit and an opposite one
2122 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2124 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2126 if ( nbNodes != 4 ) return method;
2128 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2129 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2130 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2132 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2134 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2139 // there are adjacent prism
2140 for ( int variant = 0; variant < nbVariants; ++variant )
2142 // check method compliance with adjacent prisms,
2143 // the found prism facets must be among facets of prisms described by current method
2144 method._nbSplits = nbSplits;
2145 method._nbCorners = 6;
2146 method._connectivity = connVariants[ variant ];
2147 if ( method.hasFacet( *t ))
2152 // No adjacent prisms. Select a variant with a best aspect ratio.
2154 double badness[2] = { 0., 0. };
2155 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2156 const SMDS_MeshNode** nodes = vol.GetNodes();
2157 for ( int variant = 0; variant < nbVariants; ++variant )
2158 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2160 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2161 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2163 method._connectivity = connVariants[ variant ];
2164 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2165 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2166 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2168 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2171 badness[ variant ] += getBadRate( &tria, aspectRatio );
2173 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2175 method._nbSplits = nbSplits;
2176 method._nbCorners = 6;
2177 method._connectivity = connVariants[ iBetter ];
2182 //================================================================================
2184 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2186 //================================================================================
2188 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2189 const SMDSAbs_GeometryType geom ) const
2191 // find the tetrahedron including the three nodes of facet
2192 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2193 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2194 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2195 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2196 while ( volIt1->more() )
2198 const SMDS_MeshElement* v = volIt1->next();
2199 if ( v->GetGeomType() != geom )
2201 const int lastCornerInd = v->NbCornerNodes() - 1;
2202 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2203 continue; // medium node not allowed
2204 const int ind2 = v->GetNodeIndex( n2 );
2205 if ( ind2 < 0 || lastCornerInd < ind2 )
2207 const int ind3 = v->GetNodeIndex( n3 );
2208 if ( ind3 < 0 || lastCornerInd < ind3 )
2215 //=======================================================================
2217 * \brief A key of a face of volume
2219 //=======================================================================
2221 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2223 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2225 TIDSortedNodeSet sortedNodes;
2226 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2227 int nbNodes = vol.NbFaceNodes( iF );
2228 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2229 for ( int i = 0; i < nbNodes; i += iQ )
2230 sortedNodes.insert( fNodes[i] );
2231 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2232 first.first = (*(n++))->GetID();
2233 first.second = (*(n++))->GetID();
2234 second.first = (*(n++))->GetID();
2235 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2240 //=======================================================================
2241 //function : SplitVolumes
2242 //purpose : Split volume elements into tetrahedra or prisms.
2243 // If facet ID < 0, element is split into tetrahedra,
2244 // else a hexahedron is split into prisms so that the given facet is
2245 // split into triangles
2246 //=======================================================================
2248 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2249 const int theMethodFlags)
2251 SMDS_VolumeTool volTool;
2252 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2253 fHelper.ToFixNodeParameters( true );
2255 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2256 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2258 SMESH_SequenceOfElemPtr newNodes, newElems;
2260 // map face of volume to it's baricenrtic node
2261 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2263 vector<const SMDS_MeshElement* > splitVols;
2265 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2266 for ( ; elem2facet != theElems.end(); ++elem2facet )
2268 const SMDS_MeshElement* elem = elem2facet->first;
2269 const int facetToSplit = elem2facet->second;
2270 if ( elem->GetType() != SMDSAbs_Volume )
2272 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2273 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2276 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2278 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2279 getTetraSplitMethod( volTool, theMethodFlags ) :
2280 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2281 if ( splitMethod._nbSplits < 1 ) continue;
2283 // find submesh to add new tetras to
2284 if ( !subMesh || !subMesh->Contains( elem ))
2286 int shapeID = FindShape( elem );
2287 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2288 subMesh = GetMeshDS()->MeshElements( shapeID );
2291 if ( elem->IsQuadratic() )
2294 // add quadratic links to the helper
2295 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2297 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2298 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2299 for ( int iN = 0; iN < nbN; iN += iQ )
2300 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2302 helper.SetIsQuadratic( true );
2307 helper.SetIsQuadratic( false );
2309 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2310 volTool.GetNodes() + elem->NbNodes() );
2311 helper.SetElementsOnShape( true );
2312 if ( splitMethod._baryNode )
2314 // make a node at barycenter
2315 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2316 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2317 nodes.push_back( gcNode );
2318 newNodes.push_back( gcNode );
2320 if ( !splitMethod._faceBaryNode.empty() )
2322 // make or find baricentric nodes of faces
2323 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2324 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2326 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2327 volFace2BaryNode.insert
2328 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2331 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2332 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2334 nodes.push_back( iF_n->second = f_n->second );
2339 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2340 const int* volConn = splitMethod._connectivity;
2341 if ( splitMethod._nbCorners == 4 ) // tetra
2342 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2343 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2344 nodes[ volConn[1] ],
2345 nodes[ volConn[2] ],
2346 nodes[ volConn[3] ]));
2348 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2349 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2350 nodes[ volConn[1] ],
2351 nodes[ volConn[2] ],
2352 nodes[ volConn[3] ],
2353 nodes[ volConn[4] ],
2354 nodes[ volConn[5] ]));
2356 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2358 // Split faces on sides of the split volume
2360 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2361 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2363 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2364 if ( nbNodes < 4 ) continue;
2366 // find an existing face
2367 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2368 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2369 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2370 /*noMedium=*/false))
2373 helper.SetElementsOnShape( false );
2374 vector< const SMDS_MeshElement* > triangles;
2376 // find submesh to add new triangles in
2377 if ( !fSubMesh || !fSubMesh->Contains( face ))
2379 int shapeID = FindShape( face );
2380 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2382 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2383 if ( iF_n != splitMethod._faceBaryNode.end() )
2385 const SMDS_MeshNode *baryNode = iF_n->second;
2386 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2388 const SMDS_MeshNode* n1 = fNodes[iN];
2389 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2390 const SMDS_MeshNode *n3 = baryNode;
2391 if ( !volTool.IsFaceExternal( iF ))
2393 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2395 if ( fSubMesh ) // update position of the bary node on geometry
2398 subMesh->RemoveNode( baryNode );
2399 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2400 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2401 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2403 fHelper.SetSubShape( s );
2404 gp_XY uv( 1e100, 1e100 );
2406 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2407 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2410 // node is too far from the surface
2411 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2412 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2413 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2420 // among possible triangles create ones described by split method
2421 const int* nInd = volTool.GetFaceNodesIndices( iF );
2422 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2423 int iCom = 0; // common node of triangle faces to split into
2424 list< TTriangleFacet > facets;
2425 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2427 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2428 nInd[ iQ * ( (iCom+1)%nbNodes )],
2429 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2430 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2431 nInd[ iQ * ( (iCom+2)%nbNodes )],
2432 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2433 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2435 facets.push_back( t012 );
2436 facets.push_back( t023 );
2437 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2438 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2439 nInd[ iQ * ((iLast-1)%nbNodes )],
2440 nInd[ iQ * ((iLast )%nbNodes )]));
2444 list< TTriangleFacet >::iterator facet = facets.begin();
2445 if ( facet == facets.end() )
2447 for ( ; facet != facets.end(); ++facet )
2449 if ( !volTool.IsFaceExternal( iF ))
2450 swap( facet->_n2, facet->_n3 );
2451 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2452 volNodes[ facet->_n2 ],
2453 volNodes[ facet->_n3 ]));
2456 for ( size_t i = 0; i < triangles.size(); ++i )
2458 if ( !triangles[ i ]) continue;
2460 fSubMesh->AddElement( triangles[ i ]);
2461 newElems.push_back( triangles[ i ]);
2463 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2464 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2466 } // while a face based on facet nodes exists
2467 } // loop on volume faces to split them into triangles
2469 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2471 if ( geomType == SMDSEntity_TriQuad_Hexa )
2473 // remove medium nodes that could become free
2474 for ( int i = 20; i < volTool.NbNodes(); ++i )
2475 if ( volNodes[i]->NbInverseElements() == 0 )
2476 GetMeshDS()->RemoveNode( volNodes[i] );
2478 } // loop on volumes to split
2480 myLastCreatedNodes = newNodes;
2481 myLastCreatedElems = newElems;
2484 //=======================================================================
2485 //function : GetHexaFacetsToSplit
2486 //purpose : For hexahedra that will be split into prisms, finds facets to
2487 // split into triangles. Only hexahedra adjacent to the one closest
2488 // to theFacetNormal.Location() are returned.
2489 //param [in,out] theHexas - the hexahedra
2490 //param [in] theFacetNormal - facet normal
2491 //param [out] theFacets - the hexahedra and found facet IDs
2492 //=======================================================================
2494 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2495 const gp_Ax1& theFacetNormal,
2496 TFacetOfElem & theFacets)
2498 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2500 // Find a hexa closest to the location of theFacetNormal
2502 const SMDS_MeshElement* startHex;
2504 // get SMDS_ElemIteratorPtr on theHexas
2505 typedef const SMDS_MeshElement* TValue;
2506 typedef TIDSortedElemSet::iterator TSetIterator;
2507 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2508 typedef SMDS_MeshElement::GeomFilter TFilter;
2509 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2510 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2511 ( new TElemSetIter( theHexas.begin(),
2513 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2515 SMESH_ElementSearcher* searcher =
2516 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2518 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2523 throw SALOME_Exception( THIS_METHOD "startHex not found");
2526 // Select a facet of startHex by theFacetNormal
2528 SMDS_VolumeTool vTool( startHex );
2529 double norm[3], dot, maxDot = 0;
2531 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2532 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2534 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2542 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2544 // Fill theFacets starting from facetID of startHex
2546 // facets used for searching of volumes adjacent to already treated ones
2547 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2548 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2549 TFacetMap facetsToCheck;
2551 set<const SMDS_MeshNode*> facetNodes;
2552 const SMDS_MeshElement* curHex;
2554 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2558 // move in two directions from startHex via facetID
2559 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2562 int curFacet = facetID;
2563 if ( is2nd ) // do not treat startHex twice
2565 vTool.Set( curHex );
2566 if ( vTool.IsFreeFace( curFacet, &curHex ))
2572 vTool.GetFaceNodes( curFacet, facetNodes );
2573 vTool.Set( curHex );
2574 curFacet = vTool.GetFaceIndex( facetNodes );
2579 // store a facet to split
2580 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2582 theFacets.insert( make_pair( curHex, -1 ));
2585 if ( !allHex && !theHexas.count( curHex ))
2588 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2589 theFacets.insert( make_pair( curHex, curFacet ));
2590 if ( !facetIt2isNew.second )
2593 // remember not-to-split facets in facetsToCheck
2594 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2595 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2597 if ( iF == curFacet && iF == oppFacet )
2599 TVolumeFaceKey facetKey ( vTool, iF );
2600 TElemFacets elemFacet( facetIt2isNew.first, iF );
2601 pair< TFacetMap::iterator, bool > it2isnew =
2602 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2603 if ( !it2isnew.second )
2604 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2606 // pass to a volume adjacent via oppFacet
2607 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2613 // get a new curFacet
2614 vTool.GetFaceNodes( oppFacet, facetNodes );
2615 vTool.Set( curHex );
2616 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2619 } // move in two directions from startHex via facetID
2621 // Find a new startHex by facetsToCheck
2625 TFacetMap::iterator fIt = facetsToCheck.begin();
2626 while ( !startHex && fIt != facetsToCheck.end() )
2628 const TElemFacets& elemFacets = fIt->second;
2629 const SMDS_MeshElement* hex = elemFacets.first->first;
2630 int splitFacet = elemFacets.first->second;
2631 int lateralFacet = elemFacets.second;
2632 facetsToCheck.erase( fIt );
2633 fIt = facetsToCheck.begin();
2636 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2637 curHex->GetGeomType() != SMDSGeom_HEXA )
2639 if ( !allHex && !theHexas.count( curHex ))
2644 // find a facet of startHex to split
2646 set<const SMDS_MeshNode*> lateralNodes;
2647 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2648 vTool.GetFaceNodes( splitFacet, facetNodes );
2649 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2650 vTool.Set( startHex );
2651 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2653 // look for a facet of startHex having common nodes with facetNodes
2654 // but not lateralFacet
2655 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2657 if ( iF == lateralFacet )
2659 int nbCommonNodes = 0;
2660 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2661 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2662 nbCommonNodes += facetNodes.count( nn[ iN ]);
2664 if ( nbCommonNodes >= 2 )
2671 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2673 } // while ( startHex )
2680 //================================================================================
2682 * \brief Selects nodes of several elements according to a given interlace
2683 * \param [in] srcNodes - nodes to select from
2684 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2685 * \param [in] interlace - indices of nodes for all elements
2686 * \param [in] nbElems - nb of elements
2687 * \param [in] nbNodes - nb of nodes in each element
2688 * \param [in] mesh - the mesh
2689 * \param [out] elemQueue - a list to push elements found by the selected nodes
2690 * \param [in] type - type of elements to look for
2692 //================================================================================
2694 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2695 vector< const SMDS_MeshNode* >* tgtNodesVec,
2696 const int* interlace,
2699 SMESHDS_Mesh* mesh = 0,
2700 list< const SMDS_MeshElement* >* elemQueue=0,
2701 SMDSAbs_ElementType type=SMDSAbs_All)
2703 for ( int iE = 0; iE < nbElems; ++iE )
2705 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2706 const int* select = & interlace[iE*nbNodes];
2707 elemNodes.resize( nbNodes );
2708 for ( int iN = 0; iN < nbNodes; ++iN )
2709 elemNodes[iN] = srcNodes[ select[ iN ]];
2711 const SMDS_MeshElement* e;
2713 for ( int iE = 0; iE < nbElems; ++iE )
2714 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2715 elemQueue->push_back( e );
2719 //=======================================================================
2721 * Split bi-quadratic elements into linear ones without creation of additional nodes
2722 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2723 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2724 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2725 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2726 * will be split in order to keep the mesh conformal.
2727 * \param elems - elements to split
2729 //=======================================================================
2731 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2733 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2734 vector<const SMDS_MeshElement* > splitElems;
2735 list< const SMDS_MeshElement* > elemQueue;
2736 list< const SMDS_MeshElement* >::iterator elemIt;
2738 SMESHDS_Mesh * mesh = GetMeshDS();
2739 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2740 int nbElems, nbNodes;
2742 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2743 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2746 elemQueue.push_back( *elemSetIt );
2747 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2749 const SMDS_MeshElement* elem = *elemIt;
2750 switch( elem->GetEntityType() )
2752 case SMDSEntity_TriQuad_Hexa: // HEX27
2754 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2755 nbElems = nbNodes = 8;
2756 elemType = & hexaType;
2758 // get nodes for new elements
2759 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2760 { 1,9,20,8, 17,22,26,21 },
2761 { 2,10,20,9, 18,23,26,22 },
2762 { 3,11,20,10, 19,24,26,23 },
2763 { 16,21,26,24, 4,12,25,15 },
2764 { 17,22,26,21, 5,13,25,12 },
2765 { 18,23,26,22, 6,14,25,13 },
2766 { 19,24,26,23, 7,15,25,14 }};
2767 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2769 // add boundary faces to elemQueue
2770 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2771 { 4,5,6,7, 12,13,14,15, 25 },
2772 { 0,1,5,4, 8,17,12,16, 21 },
2773 { 1,2,6,5, 9,18,13,17, 22 },
2774 { 2,3,7,6, 10,19,14,18, 23 },
2775 { 3,0,4,7, 11,16,15,19, 24 }};
2776 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2778 // add boundary segments to elemQueue
2779 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2780 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2781 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2782 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2785 case SMDSEntity_BiQuad_Triangle: // TRIA7
2787 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2790 elemType = & quadType;
2792 // get nodes for new elements
2793 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2794 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2796 // add boundary segments to elemQueue
2797 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2798 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2801 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2803 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806 elemType = & quadType;
2808 // get nodes for new elements
2809 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2810 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2812 // add boundary segments to elemQueue
2813 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2814 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2817 case SMDSEntity_Quad_Edge:
2819 if ( elemIt == elemQueue.begin() )
2820 continue; // an elem is in theElems
2821 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2824 elemType = & segType;
2826 // get nodes for new elements
2827 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2828 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2832 } // switch( elem->GetEntityType() )
2834 // Create new elements
2836 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2840 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2841 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2842 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2843 //elemType->SetID( -1 );
2845 for ( int iE = 0; iE < nbElems; ++iE )
2846 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2849 ReplaceElemInGroups( elem, splitElems, mesh );
2852 for ( size_t i = 0; i < splitElems.size(); ++i )
2853 subMesh->AddElement( splitElems[i] );
2858 //=======================================================================
2859 //function : AddToSameGroups
2860 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2861 //=======================================================================
2863 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2864 const SMDS_MeshElement* elemInGroups,
2865 SMESHDS_Mesh * aMesh)
2867 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868 if (!groups.empty()) {
2869 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2870 for ( ; grIt != groups.end(); grIt++ ) {
2871 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2872 if ( group && group->Contains( elemInGroups ))
2873 group->SMDSGroup().Add( elemToAdd );
2879 //=======================================================================
2880 //function : RemoveElemFromGroups
2881 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2882 //=======================================================================
2883 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2884 SMESHDS_Mesh * aMesh)
2886 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2887 if (!groups.empty())
2889 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2890 for (; GrIt != groups.end(); GrIt++)
2892 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2893 if (!grp || grp->IsEmpty()) continue;
2894 grp->SMDSGroup().Remove(removeelem);
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const SMDS_MeshElement* elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty()) {
2911 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2912 for ( ; grIt != groups.end(); grIt++ ) {
2913 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2914 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2915 group->SMDSGroup().Add( elemToAdd );
2920 //================================================================================
2922 * \brief Replace elemToRm by elemToAdd in the all groups
2924 //================================================================================
2926 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2927 const vector<const SMDS_MeshElement*>& elemToAdd,
2928 SMESHDS_Mesh * aMesh)
2930 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2931 if (!groups.empty())
2933 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2934 for ( ; grIt != groups.end(); grIt++ ) {
2935 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2936 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2937 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2938 group->SMDSGroup().Add( elemToAdd[ i ] );
2943 //=======================================================================
2944 //function : QuadToTri
2945 //purpose : Cut quadrangles into triangles.
2946 // theCrit is used to select a diagonal to cut
2947 //=======================================================================
2949 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2950 const bool the13Diag)
2953 myLastCreatedElems.reserve( theElems.size() * 2 );
2955 SMESHDS_Mesh * aMesh = GetMeshDS();
2956 Handle(Geom_Surface) surface;
2957 SMESH_MesherHelper helper( *GetMesh() );
2959 TIDSortedElemSet::iterator itElem;
2960 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2962 const SMDS_MeshElement* elem = *itElem;
2963 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2966 if ( elem->NbNodes() == 4 ) {
2967 // retrieve element nodes
2968 const SMDS_MeshNode* aNodes [4];
2969 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2971 while ( itN->more() )
2972 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2974 int aShapeId = FindShape( elem );
2975 const SMDS_MeshElement* newElem1 = 0;
2976 const SMDS_MeshElement* newElem2 = 0;
2978 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2979 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2982 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2983 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2985 myLastCreatedElems.push_back(newElem1);
2986 myLastCreatedElems.push_back(newElem2);
2987 // put a new triangle on the same shape and add to the same groups
2990 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2991 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2993 AddToSameGroups( newElem1, elem, aMesh );
2994 AddToSameGroups( newElem2, elem, aMesh );
2995 aMesh->RemoveElement( elem );
2998 // Quadratic quadrangle
3000 else if ( elem->NbNodes() >= 8 )
3002 // get surface elem is on
3003 int aShapeId = FindShape( elem );
3004 if ( aShapeId != helper.GetSubShapeID() ) {
3008 shape = aMesh->IndexToShape( aShapeId );
3009 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3010 TopoDS_Face face = TopoDS::Face( shape );
3011 surface = BRep_Tool::Surface( face );
3012 if ( !surface.IsNull() )
3013 helper.SetSubShape( shape );
3017 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3018 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3019 for ( int i = 0; itN->more(); ++i )
3020 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3022 const SMDS_MeshNode* centrNode = aNodes[8];
3023 if ( centrNode == 0 )
3025 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3026 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3028 myLastCreatedNodes.push_back(centrNode);
3031 // create a new element
3032 const SMDS_MeshElement* newElem1 = 0;
3033 const SMDS_MeshElement* newElem2 = 0;
3035 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3036 aNodes[6], aNodes[7], centrNode );
3037 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3038 centrNode, aNodes[4], aNodes[5] );
3041 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3042 aNodes[7], aNodes[4], centrNode );
3043 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3044 centrNode, aNodes[5], aNodes[6] );
3046 myLastCreatedElems.push_back(newElem1);
3047 myLastCreatedElems.push_back(newElem2);
3048 // put a new triangle on the same shape and add to the same groups
3051 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3052 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3054 AddToSameGroups( newElem1, elem, aMesh );
3055 AddToSameGroups( newElem2, elem, aMesh );
3056 aMesh->RemoveElement( elem );
3063 //=======================================================================
3064 //function : getAngle
3066 //=======================================================================
3068 double getAngle(const SMDS_MeshElement * tr1,
3069 const SMDS_MeshElement * tr2,
3070 const SMDS_MeshNode * n1,
3071 const SMDS_MeshNode * n2)
3073 double angle = 2. * M_PI; // bad angle
3076 SMESH::Controls::TSequenceOfXYZ P1, P2;
3077 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3078 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3081 if(!tr1->IsQuadratic())
3082 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3084 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3085 if ( N1.SquareMagnitude() <= gp::Resolution() )
3087 if(!tr2->IsQuadratic())
3088 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3090 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3091 if ( N2.SquareMagnitude() <= gp::Resolution() )
3094 // find the first diagonal node n1 in the triangles:
3095 // take in account a diagonal link orientation
3096 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3097 for ( int t = 0; t < 2; t++ ) {
3098 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3099 int i = 0, iDiag = -1;
3100 while ( it->more()) {
3101 const SMDS_MeshElement *n = it->next();
3102 if ( n == n1 || n == n2 ) {
3106 if ( i - iDiag == 1 )
3107 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3116 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3119 angle = N1.Angle( N2 );
3124 // =================================================
3125 // class generating a unique ID for a pair of nodes
3126 // and able to return nodes by that ID
3127 // =================================================
3131 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3132 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3135 long GetLinkID (const SMDS_MeshNode * n1,
3136 const SMDS_MeshNode * n2) const
3138 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3141 bool GetNodes (const long theLinkID,
3142 const SMDS_MeshNode* & theNode1,
3143 const SMDS_MeshNode* & theNode2) const
3145 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3146 if ( !theNode1 ) return false;
3147 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3148 if ( !theNode2 ) return false;
3154 const SMESHDS_Mesh* myMesh;
3159 //=======================================================================
3160 //function : TriToQuad
3161 //purpose : Fuse neighbour triangles into quadrangles.
3162 // theCrit is used to select a neighbour to fuse with.
3163 // theMaxAngle is a max angle between element normals at which
3164 // fusion is still performed.
3165 //=======================================================================
3167 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3168 SMESH::Controls::NumericalFunctorPtr theCrit,
3169 const double theMaxAngle)
3172 myLastCreatedElems.reserve( theElems.size() / 2 );
3174 if ( !theCrit.get() )
3177 SMESHDS_Mesh * aMesh = GetMeshDS();
3179 // Prepare data for algo: build
3180 // 1. map of elements with their linkIDs
3181 // 2. map of linkIDs with their elements
3183 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3184 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3185 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3186 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3188 TIDSortedElemSet::iterator itElem;
3189 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3191 const SMDS_MeshElement* elem = *itElem;
3192 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3193 bool IsTria = ( elem->NbCornerNodes()==3 );
3194 if (!IsTria) continue;
3196 // retrieve element nodes
3197 const SMDS_MeshNode* aNodes [4];
3198 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3201 aNodes[ i++ ] = itN->next();
3202 aNodes[ 3 ] = aNodes[ 0 ];
3205 for ( i = 0; i < 3; i++ ) {
3206 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3207 // check if elements sharing a link can be fused
3208 itLE = mapLi_listEl.find( link );
3209 if ( itLE != mapLi_listEl.end() ) {
3210 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3212 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3213 //if ( FindShape( elem ) != FindShape( elem2 ))
3214 // continue; // do not fuse triangles laying on different shapes
3215 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3216 continue; // avoid making badly shaped quads
3217 (*itLE).second.push_back( elem );
3220 mapLi_listEl[ link ].push_back( elem );
3222 mapEl_setLi [ elem ].insert( link );
3225 // Clean the maps from the links shared by a sole element, ie
3226 // links to which only one element is bound in mapLi_listEl
3228 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3229 int nbElems = (*itLE).second.size();
3230 if ( nbElems < 2 ) {
3231 const SMDS_MeshElement* elem = (*itLE).second.front();
3232 SMESH_TLink link = (*itLE).first;
3233 mapEl_setLi[ elem ].erase( link );
3234 if ( mapEl_setLi[ elem ].empty() )
3235 mapEl_setLi.erase( elem );
3239 // Algo: fuse triangles into quadrangles
3241 while ( ! mapEl_setLi.empty() ) {
3242 // Look for the start element:
3243 // the element having the least nb of shared links
3244 const SMDS_MeshElement* startElem = 0;
3246 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3247 int nbLinks = (*itEL).second.size();
3248 if ( nbLinks < minNbLinks ) {
3249 startElem = (*itEL).first;
3250 minNbLinks = nbLinks;
3251 if ( minNbLinks == 1 )
3256 // search elements to fuse starting from startElem or links of elements
3257 // fused earlyer - startLinks
3258 list< SMESH_TLink > startLinks;
3259 while ( startElem || !startLinks.empty() ) {
3260 while ( !startElem && !startLinks.empty() ) {
3261 // Get an element to start, by a link
3262 SMESH_TLink linkId = startLinks.front();
3263 startLinks.pop_front();
3264 itLE = mapLi_listEl.find( linkId );
3265 if ( itLE != mapLi_listEl.end() ) {
3266 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3267 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3268 for ( ; itE != listElem.end() ; itE++ )
3269 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3271 mapLi_listEl.erase( itLE );
3276 // Get candidates to be fused
3277 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3278 const SMESH_TLink *link12 = 0, *link13 = 0;
3280 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3281 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3282 ASSERT( !setLi.empty() );
3283 set< SMESH_TLink >::iterator itLi;
3284 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3286 const SMESH_TLink & link = (*itLi);
3287 itLE = mapLi_listEl.find( link );
3288 if ( itLE == mapLi_listEl.end() )
3291 const SMDS_MeshElement* elem = (*itLE).second.front();
3293 elem = (*itLE).second.back();
3294 mapLi_listEl.erase( itLE );
3295 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3306 // add other links of elem to list of links to re-start from
3307 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3308 set< SMESH_TLink >::iterator it;
3309 for ( it = links.begin(); it != links.end(); it++ ) {
3310 const SMESH_TLink& link2 = (*it);
3311 if ( link2 != link )
3312 startLinks.push_back( link2 );
3316 // Get nodes of possible quadrangles
3317 const SMDS_MeshNode *n12 [4], *n13 [4];
3318 bool Ok12 = false, Ok13 = false;
3319 const SMDS_MeshNode *linkNode1, *linkNode2;
3321 linkNode1 = link12->first;
3322 linkNode2 = link12->second;
3323 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3327 linkNode1 = link13->first;
3328 linkNode2 = link13->second;
3329 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3333 // Choose a pair to fuse
3334 if ( Ok12 && Ok13 ) {
3335 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3336 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3337 double aBadRate12 = getBadRate( &quad12, theCrit );
3338 double aBadRate13 = getBadRate( &quad13, theCrit );
3339 if ( aBadRate13 < aBadRate12 )
3346 // and remove fused elems and remove links from the maps
3347 mapEl_setLi.erase( tr1 );
3350 mapEl_setLi.erase( tr2 );
3351 mapLi_listEl.erase( *link12 );
3352 if ( tr1->NbNodes() == 3 )
3354 const SMDS_MeshElement* newElem = 0;
3355 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3356 myLastCreatedElems.push_back(newElem);
3357 AddToSameGroups( newElem, tr1, aMesh );
3358 int aShapeId = tr1->getshapeId();
3360 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3361 aMesh->RemoveElement( tr1 );
3362 aMesh->RemoveElement( tr2 );
3365 vector< const SMDS_MeshNode* > N1;
3366 vector< const SMDS_MeshNode* > N2;
3367 getNodesFromTwoTria(tr1,tr2,N1,N2);
3368 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3369 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3370 // i.e. first nodes from both arrays form a new diagonal
3371 const SMDS_MeshNode* aNodes[8];
3380 const SMDS_MeshElement* newElem = 0;
3381 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3382 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3383 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3385 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3387 myLastCreatedElems.push_back(newElem);
3388 AddToSameGroups( newElem, tr1, aMesh );
3389 int aShapeId = tr1->getshapeId();
3391 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3392 aMesh->RemoveElement( tr1 );
3393 aMesh->RemoveElement( tr2 );
3394 // remove middle node (9)
3395 if ( N1[4]->NbInverseElements() == 0 )
3396 aMesh->RemoveNode( N1[4] );
3397 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N1[6] );
3399 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3400 aMesh->RemoveNode( N2[6] );
3405 mapEl_setLi.erase( tr3 );
3406 mapLi_listEl.erase( *link13 );
3407 if ( tr1->NbNodes() == 3 ) {
3408 const SMDS_MeshElement* newElem = 0;
3409 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3410 myLastCreatedElems.push_back(newElem);
3411 AddToSameGroups( newElem, tr1, aMesh );
3412 int aShapeId = tr1->getshapeId();
3414 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3415 aMesh->RemoveElement( tr1 );
3416 aMesh->RemoveElement( tr3 );
3419 vector< const SMDS_MeshNode* > N1;
3420 vector< const SMDS_MeshNode* > N2;
3421 getNodesFromTwoTria(tr1,tr3,N1,N2);
3422 // now we receive following N1 and N2 (using numeration as above image)
3423 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3424 // i.e. first nodes from both arrays form a new diagonal
3425 const SMDS_MeshNode* aNodes[8];
3434 const SMDS_MeshElement* newElem = 0;
3435 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3436 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3437 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3439 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3441 myLastCreatedElems.push_back(newElem);
3442 AddToSameGroups( newElem, tr1, aMesh );
3443 int aShapeId = tr1->getshapeId();
3445 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3446 aMesh->RemoveElement( tr1 );
3447 aMesh->RemoveElement( tr3 );
3448 // remove middle node (9)
3449 if ( N1[4]->NbInverseElements() == 0 )
3450 aMesh->RemoveNode( N1[4] );
3451 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3452 aMesh->RemoveNode( N1[6] );
3453 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3454 aMesh->RemoveNode( N2[6] );
3458 // Next element to fuse: the rejected one
3460 startElem = Ok12 ? tr3 : tr2;
3462 } // if ( startElem )
3463 } // while ( startElem || !startLinks.empty() )
3464 } // while ( ! mapEl_setLi.empty() )
3469 //================================================================================
3471 * \brief Return nodes linked to the given one
3472 * \param theNode - the node
3473 * \param linkedNodes - the found nodes
3474 * \param type - the type of elements to check
3476 * Medium nodes are ignored
3478 //================================================================================
3480 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3481 TIDSortedElemSet & linkedNodes,
3482 SMDSAbs_ElementType type )
3484 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3485 while ( elemIt->more() )
3487 const SMDS_MeshElement* elem = elemIt->next();
3488 if(elem->GetType() == SMDSAbs_0DElement)
3491 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3492 if ( elem->GetType() == SMDSAbs_Volume )
3494 SMDS_VolumeTool vol( elem );
3495 while ( nodeIt->more() ) {
3496 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3497 if ( theNode != n && vol.IsLinked( theNode, n ))
3498 linkedNodes.insert( n );
3503 for ( int i = 0; nodeIt->more(); ++i ) {
3504 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3505 if ( n == theNode ) {
3506 int iBefore = i - 1;
3508 if ( elem->IsQuadratic() ) {
3509 int nb = elem->NbNodes() / 2;
3510 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3511 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3513 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3514 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3521 //=======================================================================
3522 //function : laplacianSmooth
3523 //purpose : pulls theNode toward the center of surrounding nodes directly
3524 // connected to that node along an element edge
3525 //=======================================================================
3527 void laplacianSmooth(const SMDS_MeshNode* theNode,
3528 const Handle(Geom_Surface)& theSurface,
3529 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3531 // find surrounding nodes
3533 TIDSortedElemSet nodeSet;
3534 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3536 // compute new coodrs
3538 double coord[] = { 0., 0., 0. };
3539 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3540 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3541 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3542 if ( theSurface.IsNull() ) { // smooth in 3D
3543 coord[0] += node->X();
3544 coord[1] += node->Y();
3545 coord[2] += node->Z();
3547 else { // smooth in 2D
3548 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3549 gp_XY* uv = theUVMap[ node ];
3550 coord[0] += uv->X();
3551 coord[1] += uv->Y();
3554 int nbNodes = nodeSet.size();
3557 coord[0] /= nbNodes;
3558 coord[1] /= nbNodes;
3560 if ( !theSurface.IsNull() ) {
3561 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3562 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3563 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3569 coord[2] /= nbNodes;
3573 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3576 //=======================================================================
3577 //function : centroidalSmooth
3578 //purpose : pulls theNode toward the element-area-weighted centroid of the
3579 // surrounding elements
3580 //=======================================================================
3582 void centroidalSmooth(const SMDS_MeshNode* theNode,
3583 const Handle(Geom_Surface)& theSurface,
3584 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3586 gp_XYZ aNewXYZ(0.,0.,0.);
3587 SMESH::Controls::Area anAreaFunc;
3588 double totalArea = 0.;
3593 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3594 while ( elemIt->more() )
3596 const SMDS_MeshElement* elem = elemIt->next();
3599 gp_XYZ elemCenter(0.,0.,0.);
3600 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3601 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3602 int nn = elem->NbNodes();
3603 if(elem->IsQuadratic()) nn = nn/2;
3605 //while ( itN->more() ) {
3607 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3609 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3610 aNodePoints.push_back( aP );
3611 if ( !theSurface.IsNull() ) { // smooth in 2D
3612 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3613 gp_XY* uv = theUVMap[ aNode ];
3614 aP.SetCoord( uv->X(), uv->Y(), 0. );
3618 double elemArea = anAreaFunc.GetValue( aNodePoints );
3619 totalArea += elemArea;
3621 aNewXYZ += elemCenter * elemArea;
3623 aNewXYZ /= totalArea;
3624 if ( !theSurface.IsNull() ) {
3625 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3626 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3631 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3634 //=======================================================================
3635 //function : getClosestUV
3636 //purpose : return UV of closest projection
3637 //=======================================================================
3639 static bool getClosestUV (Extrema_GenExtPS& projector,
3640 const gp_Pnt& point,
3643 projector.Perform( point );
3644 if ( projector.IsDone() ) {
3645 double u = 0, v = 0, minVal = DBL_MAX;
3646 for ( int i = projector.NbExt(); i > 0; i-- )
3647 if ( projector.SquareDistance( i ) < minVal ) {
3648 minVal = projector.SquareDistance( i );
3649 projector.Point( i ).Parameter( u, v );
3651 result.SetCoord( u, v );
3657 //=======================================================================
3659 //purpose : Smooth theElements during theNbIterations or until a worst
3660 // element has aspect ratio <= theTgtAspectRatio.
3661 // Aspect Ratio varies in range [1.0, inf].
3662 // If theElements is empty, the whole mesh is smoothed.
3663 // theFixedNodes contains additionally fixed nodes. Nodes built
3664 // on edges and boundary nodes are always fixed.
3665 //=======================================================================
3667 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3668 set<const SMDS_MeshNode*> & theFixedNodes,
3669 const SmoothMethod theSmoothMethod,
3670 const int theNbIterations,
3671 double theTgtAspectRatio,
3676 if ( theTgtAspectRatio < 1.0 )
3677 theTgtAspectRatio = 1.0;
3679 const double disttol = 1.e-16;
3681 SMESH::Controls::AspectRatio aQualityFunc;
3683 SMESHDS_Mesh* aMesh = GetMeshDS();
3685 if ( theElems.empty() ) {
3686 // add all faces to theElems
3687 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3688 while ( fIt->more() ) {
3689 const SMDS_MeshElement* face = fIt->next();
3690 theElems.insert( theElems.end(), face );
3693 // get all face ids theElems are on
3694 set< int > faceIdSet;
3695 TIDSortedElemSet::iterator itElem;
3697 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3698 int fId = FindShape( *itElem );
3699 // check that corresponding submesh exists and a shape is face
3701 faceIdSet.find( fId ) == faceIdSet.end() &&
3702 aMesh->MeshElements( fId )) {
3703 TopoDS_Shape F = aMesh->IndexToShape( fId );
3704 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3705 faceIdSet.insert( fId );
3708 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3710 // ===============================================
3711 // smooth elements on each TopoDS_Face separately
3712 // ===============================================
3714 SMESH_MesherHelper helper( *GetMesh() );
3716 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3717 for ( ; fId != faceIdSet.rend(); ++fId )
3719 // get face surface and submesh
3720 Handle(Geom_Surface) surface;
3721 SMESHDS_SubMesh* faceSubMesh = 0;
3724 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3725 bool isUPeriodic = false, isVPeriodic = false;
3728 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3729 surface = BRep_Tool::Surface( face );
3730 faceSubMesh = aMesh->MeshElements( *fId );
3731 fToler2 = BRep_Tool::Tolerance( face );
3732 fToler2 *= fToler2 * 10.;
3733 isUPeriodic = surface->IsUPeriodic();
3734 // if ( isUPeriodic )
3735 // surface->UPeriod();
3736 isVPeriodic = surface->IsVPeriodic();
3737 // if ( isVPeriodic )
3738 // surface->VPeriod();
3739 surface->Bounds( u1, u2, v1, v2 );
3740 helper.SetSubShape( face );
3742 // ---------------------------------------------------------
3743 // for elements on a face, find movable and fixed nodes and
3744 // compute UV for them
3745 // ---------------------------------------------------------
3746 bool checkBoundaryNodes = false;
3747 bool isQuadratic = false;
3748 set<const SMDS_MeshNode*> setMovableNodes;
3749 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3750 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3751 list< const SMDS_MeshElement* > elemsOnFace;
3753 Extrema_GenExtPS projector;
3754 GeomAdaptor_Surface surfAdaptor;
3755 if ( !surface.IsNull() ) {
3756 surfAdaptor.Load( surface );
3757 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3759 int nbElemOnFace = 0;
3760 itElem = theElems.begin();
3761 // loop on not yet smoothed elements: look for elems on a face
3762 while ( itElem != theElems.end() )
3764 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3765 break; // all elements found
3767 const SMDS_MeshElement* elem = *itElem;
3768 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3769 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3773 elemsOnFace.push_back( elem );
3774 theElems.erase( itElem++ );
3778 isQuadratic = elem->IsQuadratic();
3780 // get movable nodes of elem
3781 const SMDS_MeshNode* node;
3782 SMDS_TypeOfPosition posType;
3783 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3784 int nn = 0, nbn = elem->NbNodes();
3785 if(elem->IsQuadratic())
3787 while ( nn++ < nbn ) {
3788 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789 const SMDS_PositionPtr& pos = node->GetPosition();
3790 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3791 if (posType != SMDS_TOP_EDGE &&
3792 posType != SMDS_TOP_VERTEX &&
3793 theFixedNodes.find( node ) == theFixedNodes.end())
3795 // check if all faces around the node are on faceSubMesh
3796 // because a node on edge may be bound to face
3798 if ( faceSubMesh ) {
3799 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3800 while ( eIt->more() && all ) {
3801 const SMDS_MeshElement* e = eIt->next();
3802 all = faceSubMesh->Contains( e );
3806 setMovableNodes.insert( node );
3808 checkBoundaryNodes = true;
3810 if ( posType == SMDS_TOP_3DSPACE )
3811 checkBoundaryNodes = true;
3814 if ( surface.IsNull() )
3817 // get nodes to check UV
3818 list< const SMDS_MeshNode* > uvCheckNodes;
3819 const SMDS_MeshNode* nodeInFace = 0;
3820 itN = elem->nodesIterator();
3821 nn = 0; nbn = elem->NbNodes();
3822 if(elem->IsQuadratic())
3824 while ( nn++ < nbn ) {
3825 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3826 if ( node->GetPosition()->GetDim() == 2 )
3828 if ( uvMap.find( node ) == uvMap.end() )
3829 uvCheckNodes.push_back( node );
3830 // add nodes of elems sharing node
3831 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3832 // while ( eIt->more() ) {
3833 // const SMDS_MeshElement* e = eIt->next();
3834 // if ( e != elem ) {
3835 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3836 // while ( nIt->more() ) {
3837 // const SMDS_MeshNode* n =
3838 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3839 // if ( uvMap.find( n ) == uvMap.end() )
3840 // uvCheckNodes.push_back( n );
3846 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3847 for ( ; n != uvCheckNodes.end(); ++n ) {
3850 const SMDS_PositionPtr& pos = node->GetPosition();
3851 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3855 bool toCheck = true;
3856 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3858 // compute not existing UV
3859 bool project = ( posType == SMDS_TOP_3DSPACE );
3860 // double dist1 = DBL_MAX, dist2 = 0;
3861 // if ( posType != SMDS_TOP_3DSPACE ) {
3862 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3863 // project = dist1 > fToler2;
3865 if ( project ) { // compute new UV
3867 gp_Pnt pNode = SMESH_NodeXYZ( node );
3868 if ( !getClosestUV( projector, pNode, newUV )) {
3869 MESSAGE("Node Projection Failed " << node);
3873 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3875 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3877 // if ( posType != SMDS_TOP_3DSPACE )
3878 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3879 // if ( dist2 < dist1 )
3883 // store UV in the map
3884 listUV.push_back( uv );
3885 uvMap.insert( make_pair( node, &listUV.back() ));
3887 } // loop on not yet smoothed elements
3889 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3890 checkBoundaryNodes = true;
3892 // fix nodes on mesh boundary
3894 if ( checkBoundaryNodes ) {
3895 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3896 map< SMESH_TLink, int >::iterator link_nb;
3897 // put all elements links to linkNbMap
3898 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3899 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3900 const SMDS_MeshElement* elem = (*elemIt);
3901 int nbn = elem->NbCornerNodes();
3902 // loop on elem links: insert them in linkNbMap
3903 for ( int iN = 0; iN < nbn; ++iN ) {
3904 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3905 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3906 SMESH_TLink link( n1, n2 );
3907 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3911 // remove nodes that are in links encountered only once from setMovableNodes
3912 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3913 if ( link_nb->second == 1 ) {
3914 setMovableNodes.erase( link_nb->first.node1() );
3915 setMovableNodes.erase( link_nb->first.node2() );
3920 // -----------------------------------------------------
3921 // for nodes on seam edge, compute one more UV ( uvMap2 );
3922 // find movable nodes linked to nodes on seam and which
3923 // are to be smoothed using the second UV ( uvMap2 )
3924 // -----------------------------------------------------
3926 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3927 if ( !surface.IsNull() ) {
3928 TopExp_Explorer eExp( face, TopAbs_EDGE );
3929 for ( ; eExp.More(); eExp.Next() ) {
3930 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3931 if ( !BRep_Tool::IsClosed( edge, face ))
3933 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3934 if ( !sm ) continue;
3935 // find out which parameter varies for a node on seam
3938 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3939 if ( pcurve.IsNull() ) continue;
3940 uv1 = pcurve->Value( f );
3942 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3943 if ( pcurve.IsNull() ) continue;
3944 uv2 = pcurve->Value( f );
3945 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3947 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3948 std::swap( uv1, uv2 );
3949 // get nodes on seam and its vertices
3950 list< const SMDS_MeshNode* > seamNodes;
3951 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3952 while ( nSeamIt->more() ) {
3953 const SMDS_MeshNode* node = nSeamIt->next();
3954 if ( !isQuadratic || !IsMedium( node ))
3955 seamNodes.push_back( node );
3957 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3958 for ( ; vExp.More(); vExp.Next() ) {
3959 sm = aMesh->MeshElements( vExp.Current() );
3961 nSeamIt = sm->GetNodes();
3962 while ( nSeamIt->more() )
3963 seamNodes.push_back( nSeamIt->next() );
3966 // loop on nodes on seam
3967 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3968 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3969 const SMDS_MeshNode* nSeam = *noSeIt;
3970 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3971 if ( n_uv == uvMap.end() )
3974 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3975 // set the second UV
3976 listUV.push_back( *n_uv->second );
3977 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3978 if ( uvMap2.empty() )
3979 uvMap2 = uvMap; // copy the uvMap contents
3980 uvMap2[ nSeam ] = &listUV.back();
3982 // collect movable nodes linked to ones on seam in nodesNearSeam
3983 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3984 while ( eIt->more() ) {
3985 const SMDS_MeshElement* e = eIt->next();
3986 int nbUseMap1 = 0, nbUseMap2 = 0;
3987 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3988 int nn = 0, nbn = e->NbNodes();
3989 if(e->IsQuadratic()) nbn = nbn/2;
3990 while ( nn++ < nbn )
3992 const SMDS_MeshNode* n =
3993 static_cast<const SMDS_MeshNode*>( nIt->next() );
3995 setMovableNodes.find( n ) == setMovableNodes.end() )
3997 // add only nodes being closer to uv2 than to uv1
3998 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3999 // 0.5 * ( n->Y() + nSeam->Y() ),
4000 // 0.5 * ( n->Z() + nSeam->Z() ));
4002 // getClosestUV( projector, pMid, uv );
4003 double x = uvMap[ n ]->Coord( iPar );
4004 if ( Abs( uv1.Coord( iPar ) - x ) >
4005 Abs( uv2.Coord( iPar ) - x )) {
4006 nodesNearSeam.insert( n );
4012 // for centroidalSmooth all element nodes must
4013 // be on one side of a seam
4014 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4015 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4017 while ( nn++ < nbn ) {
4018 const SMDS_MeshNode* n =
4019 static_cast<const SMDS_MeshNode*>( nIt->next() );
4020 setMovableNodes.erase( n );
4024 } // loop on nodes on seam
4025 } // loop on edge of a face
4026 } // if ( !face.IsNull() )
4028 if ( setMovableNodes.empty() ) {
4029 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4030 continue; // goto next face
4038 double maxRatio = -1., maxDisplacement = -1.;
4039 set<const SMDS_MeshNode*>::iterator nodeToMove;
4040 for ( it = 0; it < theNbIterations; it++ ) {
4041 maxDisplacement = 0.;
4042 nodeToMove = setMovableNodes.begin();
4043 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4044 const SMDS_MeshNode* node = (*nodeToMove);
4045 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4048 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4049 if ( theSmoothMethod == LAPLACIAN )
4050 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4052 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4054 // node displacement
4055 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4056 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4057 if ( aDispl > maxDisplacement )
4058 maxDisplacement = aDispl;
4060 // no node movement => exit
4061 //if ( maxDisplacement < 1.e-16 ) {
4062 if ( maxDisplacement < disttol ) {
4063 MESSAGE("-- no node movement --");
4067 // check elements quality
4069 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4070 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4071 const SMDS_MeshElement* elem = (*elemIt);
4072 if ( !elem || elem->GetType() != SMDSAbs_Face )
4074 SMESH::Controls::TSequenceOfXYZ aPoints;
4075 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4076 double aValue = aQualityFunc.GetValue( aPoints );
4077 if ( aValue > maxRatio )
4081 if ( maxRatio <= theTgtAspectRatio ) {
4082 //MESSAGE("-- quality achieved --");
4085 if (it+1 == theNbIterations) {
4086 //MESSAGE("-- Iteration limit exceeded --");
4088 } // smoothing iterations
4090 // MESSAGE(" Face id: " << *fId <<
4091 // " Nb iterstions: " << it <<
4092 // " Displacement: " << maxDisplacement <<
4093 // " Aspect Ratio " << maxRatio);
4095 // ---------------------------------------
4096 // new nodes positions are computed,
4097 // record movement in DS and set new UV
4098 // ---------------------------------------
4099 nodeToMove = setMovableNodes.begin();
4100 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4101 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4102 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4103 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4104 if ( node_uv != uvMap.end() ) {
4105 gp_XY* uv = node_uv->second;
4107 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4111 // move medium nodes of quadratic elements
4114 vector<const SMDS_MeshNode*> nodes;
4116 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4117 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4119 const SMDS_MeshElement* QF = *elemIt;
4120 if ( QF->IsQuadratic() )
4122 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4123 SMDS_MeshElement::iterator() );
4124 nodes.push_back( nodes[0] );
4126 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4128 if ( !surface.IsNull() )
4130 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4131 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4132 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4133 xyz = surface->Value( uv.X(), uv.Y() );
4136 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4138 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4139 // we have to move a medium node
4140 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4146 } // loop on face ids
4152 //=======================================================================
4153 //function : isReverse
4154 //purpose : Return true if normal of prevNodes is not co-directied with
4155 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4156 // iNotSame is where prevNodes and nextNodes are different.
4157 // If result is true then future volume orientation is OK
4158 //=======================================================================
4160 bool isReverse(const SMDS_MeshElement* face,
4161 const vector<const SMDS_MeshNode*>& prevNodes,
4162 const vector<const SMDS_MeshNode*>& nextNodes,
4166 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4167 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4168 gp_XYZ extrDir( pN - pP ), faceNorm;
4169 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4171 return faceNorm * extrDir < 0.0;
4174 //================================================================================
4176 * \brief Assure that theElemSets[0] holds elements, not nodes
4178 //================================================================================
4180 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4182 if ( !theElemSets[0].empty() &&
4183 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4185 std::swap( theElemSets[0], theElemSets[1] );
4187 else if ( !theElemSets[1].empty() &&
4188 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4190 std::swap( theElemSets[0], theElemSets[1] );
4195 //=======================================================================
4197 * \brief Create elements by sweeping an element
4198 * \param elem - element to sweep
4199 * \param newNodesItVec - nodes generated from each node of the element
4200 * \param newElems - generated elements
4201 * \param nbSteps - number of sweeping steps
4202 * \param srcElements - to append elem for each generated element
4204 //=======================================================================
4206 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4207 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4208 list<const SMDS_MeshElement*>& newElems,
4209 const size_t nbSteps,
4210 SMESH_SequenceOfElemPtr& srcElements)
4212 SMESHDS_Mesh* aMesh = GetMeshDS();
4214 const int nbNodes = elem->NbNodes();
4215 const int nbCorners = elem->NbCornerNodes();
4216 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4217 polyhedron creation !!! */
4218 // Loop on elem nodes:
4219 // find new nodes and detect same nodes indices
4220 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4221 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4222 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4223 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4225 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4226 vector<int> sames(nbNodes);
4227 vector<bool> isSingleNode(nbNodes);
4229 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4230 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4231 const SMDS_MeshNode* node = nnIt->first;
4232 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4233 if ( listNewNodes.empty() )
4236 itNN [ iNode ] = listNewNodes.begin();
4237 prevNod[ iNode ] = node;
4238 nextNod[ iNode ] = listNewNodes.front();
4240 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4241 corner node of linear */
4242 if ( prevNod[ iNode ] != nextNod [ iNode ])
4243 nbDouble += !isSingleNode[iNode];
4245 if( iNode < nbCorners ) { // check corners only
4246 if ( prevNod[ iNode ] == nextNod [ iNode ])
4247 sames[nbSame++] = iNode;
4249 iNotSameNode = iNode;
4253 if ( nbSame == nbNodes || nbSame > 2) {
4254 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4258 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4260 // fix nodes order to have bottom normal external
4261 if ( baseType == SMDSEntity_Polygon )
4263 std::reverse( itNN.begin(), itNN.end() );
4264 std::reverse( prevNod.begin(), prevNod.end() );
4265 std::reverse( midlNod.begin(), midlNod.end() );
4266 std::reverse( nextNod.begin(), nextNod.end() );
4267 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4271 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4272 SMDS_MeshCell::applyInterlace( ind, itNN );
4273 SMDS_MeshCell::applyInterlace( ind, prevNod );
4274 SMDS_MeshCell::applyInterlace( ind, nextNod );
4275 SMDS_MeshCell::applyInterlace( ind, midlNod );
4276 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4279 sames[nbSame] = iNotSameNode;
4280 for ( int j = 0; j <= nbSame; ++j )
4281 for ( size_t i = 0; i < ind.size(); ++i )
4282 if ( ind[i] == sames[j] )
4287 iNotSameNode = sames[nbSame];
4291 else if ( elem->GetType() == SMDSAbs_Edge )
4293 // orient a new face same as adjacent one
4295 const SMDS_MeshElement* e;
4296 TIDSortedElemSet dummy;
4297 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4298 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4299 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4301 // there is an adjacent face, check order of nodes in it
4302 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4305 std::swap( itNN[0], itNN[1] );
4306 std::swap( prevNod[0], prevNod[1] );
4307 std::swap( nextNod[0], nextNod[1] );
4308 std::swap( isSingleNode[0], isSingleNode[1] );
4310 sames[0] = 1 - sames[0];
4311 iNotSameNode = 1 - iNotSameNode;
4316 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4318 iSameNode = sames[ nbSame-1 ];
4319 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4320 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4321 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4324 if ( baseType == SMDSEntity_Polygon )
4326 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4327 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4329 else if ( baseType == SMDSEntity_Quad_Polygon )
4331 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4332 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4335 // make new elements
4336 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4339 for ( iNode = 0; iNode < nbNodes; iNode++ )
4341 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4342 nextNod[ iNode ] = *itNN[ iNode ]++;
4345 SMDS_MeshElement* aNewElem = 0;
4346 /*if(!elem->IsPoly())*/ {
4347 switch ( baseType ) {
4349 case SMDSEntity_Node: { // sweep NODE
4350 if ( nbSame == 0 ) {
4351 if ( isSingleNode[0] )
4352 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4354 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4360 case SMDSEntity_Edge: { // sweep EDGE
4361 if ( nbDouble == 0 )
4363 if ( nbSame == 0 ) // ---> quadrangle
4364 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4365 nextNod[ 1 ], nextNod[ 0 ] );
4366 else // ---> triangle
4367 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4368 nextNod[ iNotSameNode ] );
4370 else // ---> polygon
4372 vector<const SMDS_MeshNode*> poly_nodes;
4373 poly_nodes.push_back( prevNod[0] );
4374 poly_nodes.push_back( prevNod[1] );
4375 if ( prevNod[1] != nextNod[1] )
4377 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4378 poly_nodes.push_back( nextNod[1] );
4380 if ( prevNod[0] != nextNod[0] )
4382 poly_nodes.push_back( nextNod[0] );
4383 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4385 switch ( poly_nodes.size() ) {
4387 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4390 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4391 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4394 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4399 case SMDSEntity_Triangle: // TRIANGLE --->
4401 if ( nbDouble > 0 ) break;
4402 if ( nbSame == 0 ) // ---> pentahedron
4403 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4404 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4406 else if ( nbSame == 1 ) // ---> pyramid
4407 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4408 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4409 nextNod[ iSameNode ]);
4411 else // 2 same nodes: ---> tetrahedron
4412 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4413 nextNod[ iNotSameNode ]);
4416 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4420 if ( nbDouble+nbSame == 2 )
4422 if(nbSame==0) { // ---> quadratic quadrangle
4423 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4424 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4426 else { //(nbSame==1) // ---> quadratic triangle
4428 return; // medium node on axis
4430 else if(sames[0]==0)
4431 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4432 prevNod[2], midlNod[1], nextNod[2] );
4434 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4435 prevNod[2], nextNod[2], midlNod[0]);
4438 else if ( nbDouble == 3 )
4440 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4441 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4442 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4449 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4450 if ( nbDouble > 0 ) break;
4452 if ( nbSame == 0 ) // ---> hexahedron
4453 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4454 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4456 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4457 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4458 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4459 nextNod[ iSameNode ]);
4460 newElems.push_back( aNewElem );
4461 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4462 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4463 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4465 else if ( nbSame == 2 ) { // ---> pentahedron
4466 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4467 // iBeforeSame is same too
4468 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4469 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4470 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4472 // iAfterSame is same too
4473 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4474 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4475 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4479 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4480 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4481 if ( nbDouble+nbSame != 3 ) break;
4483 // ---> pentahedron with 15 nodes
4484 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4485 nextNod[0], nextNod[1], nextNod[2],
4486 prevNod[3], prevNod[4], prevNod[5],
4487 nextNod[3], nextNod[4], nextNod[5],
4488 midlNod[0], midlNod[1], midlNod[2]);
4490 else if(nbSame==1) {
4491 // ---> 2d order pyramid of 13 nodes
4492 int apex = iSameNode;
4493 int i0 = ( apex + 1 ) % nbCorners;
4494 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4498 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4499 nextNod[i0], nextNod[i1], prevNod[apex],
4500 prevNod[i01], midlNod[i0],
4501 nextNod[i01], midlNod[i1],
4502 prevNod[i1a], prevNod[i0a],
4503 nextNod[i0a], nextNod[i1a]);
4505 else if(nbSame==2) {
4506 // ---> 2d order tetrahedron of 10 nodes
4507 int n1 = iNotSameNode;
4508 int n2 = ( n1 + 1 ) % nbCorners;
4509 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4513 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4514 prevNod[n12], prevNod[n23], prevNod[n31],
4515 midlNod[n1], nextNod[n12], nextNod[n31]);
4519 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4521 if ( nbDouble != 4 ) break;
4522 // ---> hexahedron with 20 nodes
4523 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4524 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4525 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4526 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4527 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4529 else if(nbSame==1) {
4530 // ---> pyramid + pentahedron - can not be created since it is needed
4531 // additional middle node at the center of face
4532 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4535 else if( nbSame == 2 ) {
4536 if ( nbDouble != 2 ) break;
4537 // ---> 2d order Pentahedron with 15 nodes
4539 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4540 // iBeforeSame is same too
4547 // iAfterSame is same too
4557 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4558 prevNod[n4], prevNod[n5], nextNod[n5],
4559 prevNod[n12], midlNod[n2], nextNod[n12],
4560 prevNod[n45], midlNod[n5], nextNod[n45],
4561 prevNod[n14], prevNod[n25], nextNod[n25]);
4565 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4567 if( nbSame == 0 && nbDouble == 9 ) {
4568 // ---> tri-quadratic hexahedron with 27 nodes
4569 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4570 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4571 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4572 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4573 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4574 prevNod[8], // bottom center
4575 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4576 nextNod[8], // top center
4577 midlNod[8]);// elem center
4585 case SMDSEntity_Polygon: { // sweep POLYGON
4587 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4588 // ---> hexagonal prism
4589 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4590 prevNod[3], prevNod[4], prevNod[5],
4591 nextNod[0], nextNod[1], nextNod[2],
4592 nextNod[3], nextNod[4], nextNod[5]);
4596 case SMDSEntity_Ball:
4601 } // switch ( baseType )
4604 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4606 if ( baseType != SMDSEntity_Polygon )
4608 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4609 SMDS_MeshCell::applyInterlace( ind, prevNod );
4610 SMDS_MeshCell::applyInterlace( ind, nextNod );
4611 SMDS_MeshCell::applyInterlace( ind, midlNod );
4612 SMDS_MeshCell::applyInterlace( ind, itNN );
4613 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4614 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4616 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4617 vector<int> quantities (nbNodes + 2);
4618 polyedre_nodes.clear();
4622 for (int inode = 0; inode < nbNodes; inode++)
4623 polyedre_nodes.push_back( prevNod[inode] );
4624 quantities.push_back( nbNodes );
4627 polyedre_nodes.push_back( nextNod[0] );
4628 for (int inode = nbNodes; inode-1; --inode )
4629 polyedre_nodes.push_back( nextNod[inode-1] );
4630 quantities.push_back( nbNodes );
4638 const int iQuad = elem->IsQuadratic();
4639 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4641 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4642 int inextface = (iface+1+iQuad) % nbNodes;
4643 int imid = (iface+1) % nbNodes;
4644 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4645 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4646 polyedre_nodes.push_back( prevNod[iface] ); // 1
4647 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4649 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4650 polyedre_nodes.push_back( nextNod[iface] ); // 2
4652 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4653 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4655 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4656 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4658 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4659 if ( nbFaceNodes > 2 )
4660 quantities.push_back( nbFaceNodes );
4661 else // degenerated face
4662 polyedre_nodes.resize( prevNbNodes );
4664 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4666 } // try to create a polyherdal prism
4669 newElems.push_back( aNewElem );
4670 myLastCreatedElems.push_back(aNewElem);
4671 srcElements.push_back( elem );
4674 // set new prev nodes
4675 for ( iNode = 0; iNode < nbNodes; iNode++ )
4676 prevNod[ iNode ] = nextNod[ iNode ];
4681 //=======================================================================
4683 * \brief Create 1D and 2D elements around swept elements
4684 * \param mapNewNodes - source nodes and ones generated from them
4685 * \param newElemsMap - source elements and ones generated from them
4686 * \param elemNewNodesMap - nodes generated from each node of each element
4687 * \param elemSet - all swept elements
4688 * \param nbSteps - number of sweeping steps
4689 * \param srcElements - to append elem for each generated element
4691 //=======================================================================
4693 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4694 TTElemOfElemListMap & newElemsMap,
4695 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4696 TIDSortedElemSet& elemSet,
4698 SMESH_SequenceOfElemPtr& srcElements)
4700 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4701 SMESHDS_Mesh* aMesh = GetMeshDS();
4703 // Find nodes belonging to only one initial element - sweep them into edges.
4705 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4706 for ( ; nList != mapNewNodes.end(); nList++ )
4708 const SMDS_MeshNode* node =
4709 static_cast<const SMDS_MeshNode*>( nList->first );
4710 if ( newElemsMap.count( node ))
4711 continue; // node was extruded into edge
4712 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4713 int nbInitElems = 0;
4714 const SMDS_MeshElement* el = 0;
4715 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4716 while ( eIt->more() && nbInitElems < 2 ) {
4717 const SMDS_MeshElement* e = eIt->next();
4718 SMDSAbs_ElementType type = e->GetType();
4719 if ( type == SMDSAbs_Volume ||
4723 if ( type > highType ) {
4730 if ( nbInitElems == 1 ) {
4731 bool NotCreateEdge = el && el->IsMediumNode(node);
4732 if(!NotCreateEdge) {
4733 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4734 list<const SMDS_MeshElement*> newEdges;
4735 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4740 // Make a ceiling for each element ie an equal element of last new nodes.
4741 // Find free links of faces - make edges and sweep them into faces.
4743 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4745 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4746 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4747 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4749 const SMDS_MeshElement* elem = itElem->first;
4750 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4752 if(itElem->second.size()==0) continue;
4754 const bool isQuadratic = elem->IsQuadratic();
4756 if ( elem->GetType() == SMDSAbs_Edge ) {
4757 // create a ceiling edge
4758 if ( !isQuadratic ) {
4759 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4760 vecNewNodes[ 1 ]->second.back())) {
4761 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4762 vecNewNodes[ 1 ]->second.back()));
4763 srcElements.push_back( elem );
4767 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4768 vecNewNodes[ 1 ]->second.back(),
4769 vecNewNodes[ 2 ]->second.back())) {
4770 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4771 vecNewNodes[ 1 ]->second.back(),
4772 vecNewNodes[ 2 ]->second.back()));
4773 srcElements.push_back( elem );
4777 if ( elem->GetType() != SMDSAbs_Face )
4780 bool hasFreeLinks = false;
4782 TIDSortedElemSet avoidSet;
4783 avoidSet.insert( elem );
4785 set<const SMDS_MeshNode*> aFaceLastNodes;
4786 int iNode, nbNodes = vecNewNodes.size();
4787 if ( !isQuadratic ) {
4788 // loop on the face nodes
4789 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4790 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4791 // look for free links of the face
4792 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4793 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4794 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4795 // check if a link n1-n2 is free
4796 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4797 hasFreeLinks = true;
4798 // make a new edge and a ceiling for a new edge
4799 const SMDS_MeshElement* edge;
4800 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4801 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4802 srcElements.push_back( myLastCreatedElems.back() );
4804 n1 = vecNewNodes[ iNode ]->second.back();
4805 n2 = vecNewNodes[ iNext ]->second.back();
4806 if ( !aMesh->FindEdge( n1, n2 )) {
4807 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4808 srcElements.push_back( edge );
4813 else { // elem is quadratic face
4814 int nbn = nbNodes/2;
4815 for ( iNode = 0; iNode < nbn; iNode++ ) {
4816 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4817 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4818 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4819 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4820 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4821 // check if a link is free
4822 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4823 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4824 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4825 hasFreeLinks = true;
4826 // make an edge and a ceiling for a new edge
4828 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4829 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4830 srcElements.push_back( elem );
4832 n1 = vecNewNodes[ iNode ]->second.back();
4833 n2 = vecNewNodes[ iNext ]->second.back();
4834 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4835 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4836 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4837 srcElements.push_back( elem );
4841 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4842 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4846 // sweep free links into faces
4848 if ( hasFreeLinks ) {
4849 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4850 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4852 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4853 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4854 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4855 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4856 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4858 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4859 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4860 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4862 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4863 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4864 std::advance( v, volNb );
4865 // find indices of free faces of a volume and their source edges
4866 list< int > freeInd;
4867 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4868 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4869 int iF, nbF = vTool.NbFaces();
4870 for ( iF = 0; iF < nbF; iF ++ ) {
4871 if ( vTool.IsFreeFace( iF ) &&
4872 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4873 initNodeSet != faceNodeSet) // except an initial face
4875 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4877 if ( faceNodeSet == initNodeSetNoCenter )
4879 freeInd.push_back( iF );
4880 // find source edge of a free face iF
4881 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4882 vector<const SMDS_MeshNode*>::iterator lastCommom;
4883 commonNodes.resize( nbNodes, 0 );
4884 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4885 initNodeSet.begin(), initNodeSet.end(),
4886 commonNodes.begin());
4887 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4888 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4890 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4892 if ( !srcEdges.back() )
4894 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4895 << iF << " of volume #" << vTool.ID() << endl;
4900 if ( freeInd.empty() )
4903 // create wall faces for all steps;
4904 // if such a face has been already created by sweep of edge,
4905 // assure that its orientation is OK
4906 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4908 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4909 vTool.SetExternalNormal();
4910 const int nextShift = vTool.IsForward() ? +1 : -1;
4911 list< int >::iterator ind = freeInd.begin();
4912 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4913 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4915 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4916 int nbn = vTool.NbFaceNodes( *ind );
4917 const SMDS_MeshElement * f = 0;
4918 if ( nbn == 3 ) ///// triangle
4920 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4922 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4924 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4926 nodes[ 1 + nextShift ] };
4928 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4930 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4934 else if ( nbn == 4 ) ///// quadrangle
4936 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4938 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4940 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4941 nodes[ 2 ], nodes[ 2+nextShift ] };
4943 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4945 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4946 newOrder[ 2 ], newOrder[ 3 ]));
4949 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4951 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4953 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4955 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4957 nodes[2 + 2*nextShift],
4958 nodes[3 - 2*nextShift],
4960 nodes[3 + 2*nextShift]};
4962 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4964 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4972 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4974 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4975 nodes[1], nodes[3], nodes[5], nodes[7] );
4977 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4979 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4980 nodes[4 - 2*nextShift],
4982 nodes[4 + 2*nextShift],
4984 nodes[5 - 2*nextShift],
4986 nodes[5 + 2*nextShift] };
4988 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4990 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4991 newOrder[ 2 ], newOrder[ 3 ],
4992 newOrder[ 4 ], newOrder[ 5 ],
4993 newOrder[ 6 ], newOrder[ 7 ]));
4996 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4998 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4999 SMDSAbs_Face, /*noMedium=*/false);
5001 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5003 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5004 nodes[4 - 2*nextShift],
5006 nodes[4 + 2*nextShift],
5008 nodes[5 - 2*nextShift],
5010 nodes[5 + 2*nextShift],
5013 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5015 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5016 newOrder[ 2 ], newOrder[ 3 ],
5017 newOrder[ 4 ], newOrder[ 5 ],
5018 newOrder[ 6 ], newOrder[ 7 ],
5022 else //////// polygon
5024 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5025 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5027 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5029 if ( !vTool.IsForward() )
5030 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5032 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5034 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5038 while ( srcElements.size() < myLastCreatedElems.size() )
5039 srcElements.push_back( *srcEdge );
5041 } // loop on free faces
5043 // go to the next volume
5045 while ( iVol++ < nbVolumesByStep ) v++;
5048 } // loop on volumes of one step
5049 } // sweep free links into faces
5051 // Make a ceiling face with a normal external to a volume
5053 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5054 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5055 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5057 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5058 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5059 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5063 lastVol.SetExternalNormal();
5064 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5065 const int nbn = lastVol.NbFaceNodes( iF );
5066 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5067 if ( !hasFreeLinks ||
5068 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5070 const vector<int>& interlace =
5071 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5072 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5074 AddElement( nodeVec, anyFace.Init( elem ));
5076 while ( srcElements.size() < myLastCreatedElems.size() )
5077 srcElements.push_back( elem );
5080 } // loop on swept elements
5083 //=======================================================================
5084 //function : RotationSweep
5086 //=======================================================================
5088 SMESH_MeshEditor::PGroupIDs
5089 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5090 const gp_Ax1& theAxis,
5091 const double theAngle,
5092 const int theNbSteps,
5093 const double theTol,
5094 const bool theMakeGroups,
5095 const bool theMakeWalls)
5099 setElemsFirst( theElemSets );
5100 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5101 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5103 // source elements for each generated one
5104 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5105 srcElems.reserve( theElemSets[0].size() );
5106 srcNodes.reserve( theElemSets[1].size() );
5109 aTrsf.SetRotation( theAxis, theAngle );
5111 aTrsf2.SetRotation( theAxis, theAngle/2. );
5113 gp_Lin aLine( theAxis );
5114 double aSqTol = theTol * theTol;
5116 SMESHDS_Mesh* aMesh = GetMeshDS();
5118 TNodeOfNodeListMap mapNewNodes;
5119 TElemOfVecOfNnlmiMap mapElemNewNodes;
5120 TTElemOfElemListMap newElemsMap;
5122 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5123 myMesh->NbFaces(ORDER_QUADRATIC) +
5124 myMesh->NbVolumes(ORDER_QUADRATIC) );
5125 // loop on theElemSets
5126 TIDSortedElemSet::iterator itElem;
5127 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5129 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5130 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5131 const SMDS_MeshElement* elem = *itElem;
5132 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5134 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5135 newNodesItVec.reserve( elem->NbNodes() );
5137 // loop on elem nodes
5138 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5139 while ( itN->more() )
5141 const SMDS_MeshNode* node = cast2Node( itN->next() );
5143 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5145 aXYZ.Coord( coord[0], coord[1], coord[2] );
5146 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5148 // check if a node has been already sweeped
5149 TNodeOfNodeListMapItr nIt =
5150 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5151 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5152 if ( listNewNodes.empty() )
5154 // check if we are to create medium nodes between corner ones
5155 bool needMediumNodes = false;
5156 if ( isQuadraticMesh )
5158 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5159 while (it->more() && !needMediumNodes )
5161 const SMDS_MeshElement* invElem = it->next();
5162 if ( invElem != elem && !theElems.count( invElem )) continue;
5163 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5164 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5165 needMediumNodes = true;
5170 const SMDS_MeshNode * newNode = node;
5171 for ( int i = 0; i < theNbSteps; i++ ) {
5173 if ( needMediumNodes ) // create a medium node
5175 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5176 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5177 myLastCreatedNodes.push_back(newNode);
5178 srcNodes.push_back( node );
5179 listNewNodes.push_back( newNode );
5180 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5183 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5185 // create a corner node
5186 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5187 myLastCreatedNodes.push_back(newNode);
5188 srcNodes.push_back( node );
5189 listNewNodes.push_back( newNode );
5192 listNewNodes.push_back( newNode );
5193 // if ( needMediumNodes )
5194 // listNewNodes.push_back( newNode );
5198 newNodesItVec.push_back( nIt );
5200 // make new elements
5201 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5206 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5208 PGroupIDs newGroupIDs;
5209 if ( theMakeGroups )
5210 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5215 //=======================================================================
5216 //function : ExtrusParam
5217 //purpose : standard construction
5218 //=======================================================================
5220 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5221 const int theNbSteps,
5222 const std::list<double>& theScales,
5223 const std::list<double>& theAngles,
5224 const gp_XYZ* theBasePoint,
5226 const double theTolerance):
5228 myBaseP( Precision::Infinite(), 0, 0 ),
5229 myFlags( theFlags ),
5230 myTolerance( theTolerance ),
5231 myElemsToUse( NULL )
5233 mySteps = new TColStd_HSequenceOfReal;
5234 const double stepSize = theStep.Magnitude();
5235 for (int i=1; i<=theNbSteps; i++ )
5236 mySteps->Append( stepSize );
5238 if ( !theScales.empty() )
5240 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5241 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5243 // add medium scales
5244 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5245 myScales.reserve( theNbSteps * 2 );
5246 myScales.push_back( 0.5 * ( *s1 + 1. ));
5247 myScales.push_back( *s1 );
5248 for ( ; s2 != theScales.end(); s1 = s2++ )
5250 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5251 myScales.push_back( *s2 );
5255 if ( !theAngles.empty() )
5257 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5258 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5259 linearAngleVariation( theNbSteps, angles );
5261 // accumulate angles
5264 std::list<double>::iterator a1 = angles.begin(), a2;
5265 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5270 while ( nbAngles++ < theNbSteps )
5271 angles.push_back( angles.back() );
5273 // add medium angles
5274 a2 = angles.begin(), a1 = a2++;
5275 myAngles.push_back( 0.5 * *a1 );
5276 myAngles.push_back( *a1 );
5277 for ( ; a2 != angles.end(); a1 = a2++ )
5279 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5280 myAngles.push_back( *a2 );
5286 myBaseP = *theBasePoint;
5289 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5290 ( theTolerance > 0 ))
5292 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5296 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5300 //=======================================================================
5301 //function : ExtrusParam
5302 //purpose : steps are given explicitly
5303 //=======================================================================
5305 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5306 Handle(TColStd_HSequenceOfReal) theSteps,
5308 const double theTolerance):
5310 mySteps( theSteps ),
5311 myFlags( theFlags ),
5312 myTolerance( theTolerance ),
5313 myElemsToUse( NULL )
5315 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5316 ( theTolerance > 0 ))
5318 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5322 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5326 //=======================================================================
5327 //function : ExtrusParam
5328 //purpose : for extrusion by normal
5329 //=======================================================================
5331 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5332 const int theNbSteps,
5336 mySteps( new TColStd_HSequenceOfReal ),
5337 myFlags( theFlags ),
5339 myElemsToUse( NULL )
5341 for (int i = 0; i < theNbSteps; i++ )
5342 mySteps->Append( theStepSize );
5346 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5350 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5354 //=======================================================================
5355 //function : ExtrusParam
5356 //purpose : for extrusion along path
5357 //=======================================================================
5359 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5360 const gp_Pnt* theBasePoint,
5361 const std::list<double>& theScales,
5362 const bool theMakeGroups )
5363 : myBaseP( Precision::Infinite(), 0, 0 ),
5364 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5365 myPathPoints( thePoints )
5369 myBaseP = theBasePoint->XYZ();
5372 if ( !theScales.empty() )
5374 // add medium scales
5375 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5376 myScales.reserve( thePoints.size() * 2 );
5377 myScales.push_back( 0.5 * ( 1. + *s1 ));
5378 myScales.push_back( *s1 );
5379 for ( ; s2 != theScales.end(); s1 = s2++ )
5381 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5382 myScales.push_back( *s2 );
5386 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5389 //=======================================================================
5390 //function : ExtrusParam::SetElementsToUse
5391 //purpose : stores elements to use for extrusion by normal, depending on
5392 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5393 // define myBaseP for scaling
5394 //=======================================================================
5396 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5397 const TIDSortedElemSet& nodes )
5399 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5401 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5403 myBaseP.SetCoord( 0.,0.,0. );
5404 TIDSortedElemSet newNodes;
5406 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5407 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5409 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5410 TIDSortedElemSet::const_iterator itElem = elements.begin();
5411 for ( ; itElem != elements.end(); itElem++ )
5413 const SMDS_MeshElement* elem = *itElem;
5414 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5415 while ( itN->more() ) {
5416 const SMDS_MeshElement* node = itN->next();
5417 if ( newNodes.insert( node ).second )
5418 myBaseP += SMESH_NodeXYZ( node );
5422 myBaseP /= newNodes.size();
5426 //=======================================================================
5427 //function : ExtrusParam::beginStepIter
5428 //purpose : prepare iteration on steps
5429 //=======================================================================
5431 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5433 myWithMediumNodes = withMediumNodes;
5437 //=======================================================================
5438 //function : ExtrusParam::moreSteps
5439 //purpose : are there more steps?
5440 //=======================================================================
5442 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5444 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5446 //=======================================================================
5447 //function : ExtrusParam::nextStep
5448 //purpose : returns the next step
5449 //=======================================================================
5451 double SMESH_MeshEditor::ExtrusParam::nextStep()
5454 if ( !myCurSteps.empty() )
5456 res = myCurSteps.back();
5457 myCurSteps.pop_back();
5459 else if ( myNextStep <= mySteps->Length() )
5461 myCurSteps.push_back( mySteps->Value( myNextStep ));
5463 if ( myWithMediumNodes )
5465 myCurSteps.back() /= 2.;
5466 myCurSteps.push_back( myCurSteps.back() );
5473 //=======================================================================
5474 //function : ExtrusParam::makeNodesByDir
5475 //purpose : create nodes for standard extrusion
5476 //=======================================================================
5478 int SMESH_MeshEditor::ExtrusParam::
5479 makeNodesByDir( SMESHDS_Mesh* mesh,
5480 const SMDS_MeshNode* srcNode,
5481 std::list<const SMDS_MeshNode*> & newNodes,
5482 const bool makeMediumNodes)
5484 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5487 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5489 p += myDir.XYZ() * nextStep();
5490 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5491 newNodes.push_back( newNode );
5494 if ( !myScales.empty() || !myAngles.empty() )
5496 gp_XYZ center = myBaseP;
5497 gp_Ax1 ratationAxis( center, myDir );
5500 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5501 size_t i = !makeMediumNodes;
5502 for ( beginStepIter( makeMediumNodes );
5504 ++nIt, i += 1 + !makeMediumNodes )
5506 center += myDir.XYZ() * nextStep();
5508 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5510 if ( i < myScales.size() )
5512 xyz = ( myScales[i] * ( xyz - center )) + center;
5515 if ( !myAngles.empty() )
5517 rotation.SetRotation( ratationAxis, myAngles[i] );
5518 rotation.Transforms( xyz );
5522 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5530 //=======================================================================
5531 //function : ExtrusParam::makeNodesByDirAndSew
5532 //purpose : create nodes for standard extrusion with sewing
5533 //=======================================================================
5535 int SMESH_MeshEditor::ExtrusParam::
5536 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5537 const SMDS_MeshNode* srcNode,
5538 std::list<const SMDS_MeshNode*> & newNodes,
5539 const bool makeMediumNodes)
5541 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5544 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5546 P1 += myDir.XYZ() * nextStep();
5548 // try to search in sequence of existing nodes
5549 // if myNodes.size()>0 we 'nave to use given sequence
5550 // else - use all nodes of mesh
5551 const SMDS_MeshNode * node = 0;
5552 if ( myNodes.Length() > 0 )
5554 for ( int i = 1; i <= myNodes.Length(); i++ )
5556 SMESH_NodeXYZ P2 = myNodes.Value(i);
5557 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5559 node = myNodes.Value(i);
5566 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5569 SMESH_NodeXYZ P2 = itn->next();
5570 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5579 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5581 newNodes.push_back( node );
5588 //=======================================================================
5589 //function : ExtrusParam::makeNodesByNormal2D
5590 //purpose : create nodes for extrusion using normals of faces
5591 //=======================================================================
5593 int SMESH_MeshEditor::ExtrusParam::
5594 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5595 const SMDS_MeshNode* srcNode,
5596 std::list<const SMDS_MeshNode*> & newNodes,
5597 const bool makeMediumNodes)
5599 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5601 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5603 // get normals to faces sharing srcNode
5604 vector< gp_XYZ > norms, baryCenters;
5605 gp_XYZ norm, avgNorm( 0,0,0 );
5606 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5607 while ( faceIt->more() )
5609 const SMDS_MeshElement* face = faceIt->next();
5610 if ( myElemsToUse && !myElemsToUse->count( face ))
5612 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5614 norms.push_back( norm );
5616 if ( !alongAvgNorm )
5620 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5621 bc += SMESH_NodeXYZ( nIt->next() );
5622 baryCenters.push_back( bc / nbN );
5627 if ( norms.empty() ) return 0;
5629 double normSize = avgNorm.Modulus();
5630 if ( normSize < std::numeric_limits<double>::min() )
5633 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5636 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5639 avgNorm /= normSize;
5642 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5645 double stepSize = nextStep();
5647 if ( norms.size() > 1 )
5649 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5651 // translate plane of a face
5652 baryCenters[ iF ] += norms[ iF ] * stepSize;
5654 // find point of intersection of the face plane located at baryCenters[ iF ]
5655 // and avgNorm located at pNew
5656 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5657 double dot = ( norms[ iF ] * avgNorm );
5658 if ( dot < std::numeric_limits<double>::min() )
5659 dot = stepSize * 1e-3;
5660 double step = -( norms[ iF ] * pNew + d ) / dot;
5661 pNew += step * avgNorm;
5666 pNew += stepSize * avgNorm;
5670 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5671 newNodes.push_back( newNode );
5676 //=======================================================================
5677 //function : ExtrusParam::makeNodesByNormal1D
5678 //purpose : create nodes for extrusion using normals of edges
5679 //=======================================================================
5681 int SMESH_MeshEditor::ExtrusParam::
5682 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5683 const SMDS_MeshNode* /*srcNode*/,
5684 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5685 const bool /*makeMediumNodes*/)
5687 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5691 //=======================================================================
5692 //function : ExtrusParam::makeNodesAlongTrack
5693 //purpose : create nodes for extrusion along path
5694 //=======================================================================
5696 int SMESH_MeshEditor::ExtrusParam::
5697 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5698 const SMDS_MeshNode* srcNode,
5699 std::list<const SMDS_MeshNode*> & newNodes,
5700 const bool makeMediumNodes)
5702 const Standard_Real aTolAng=1.e-4;
5704 gp_Pnt aV0x = myBaseP;
5705 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5707 const PathPoint& aPP0 = myPathPoints[0];
5708 gp_Pnt aP0x = aPP0.myPnt;
5709 gp_Dir aDT0x= aPP0.myTgt;
5711 std::vector< gp_Pnt > centers;
5712 centers.reserve( NbSteps() * 2 );
5714 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5716 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5718 const PathPoint& aPP = myPathPoints[j];
5719 const gp_Pnt& aP1x = aPP.myPnt;
5720 const gp_Dir& aDT1x = aPP.myTgt;
5723 gp_Vec aV01x( aP0x, aP1x );
5724 aTrsf.SetTranslation( aV01x );
5725 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5726 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5728 // rotation 1 [ T1,T0 ]
5729 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5730 if ( fabs( aAngleT1T0 ) > aTolAng )
5732 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5733 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5735 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5739 if ( aPP.myAngle != 0. )
5741 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5742 aPN1 = aPN1.Transformed( aTrsfRot );
5746 if ( makeMediumNodes )
5748 // create additional node
5749 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5750 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5751 newNodes.push_back( newNode );
5754 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5755 newNodes.push_back( newNode );
5757 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5758 centers.push_back( aV1x );
5767 if ( !myScales.empty() )
5770 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5771 for ( size_t i = !makeMediumNodes;
5772 i < myScales.size() && node != newNodes.end();
5773 i += ( 1 + !makeMediumNodes ), ++node )
5775 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5776 gp_Pnt aN = SMESH_NodeXYZ( *node );
5777 gp_Pnt aP = aN.Transformed( aTrsfScale );
5778 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5782 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5785 //=======================================================================
5786 //function : ExtrusionSweep
5788 //=======================================================================
5790 SMESH_MeshEditor::PGroupIDs
5791 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5792 const gp_Vec& theStep,
5793 const int theNbSteps,
5794 TTElemOfElemListMap& newElemsMap,
5796 const double theTolerance)
5798 std::list<double> dummy;
5799 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5800 theFlags, theTolerance );
5801 return ExtrusionSweep( theElems, aParams, newElemsMap );
5807 //=======================================================================
5808 //function : getOriFactor
5809 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5810 // edge curve orientation
5811 //=======================================================================
5813 double getOriFactor( const TopoDS_Edge& edge,
5814 const SMDS_MeshNode* n1,
5815 const SMDS_MeshNode* n2,
5816 SMESH_MesherHelper& helper)
5818 double u1 = helper.GetNodeU( edge, n1, n2 );
5819 double u2 = helper.GetNodeU( edge, n2, n1 );
5820 return u1 < u2 ? 1. : -1.;
5824 //=======================================================================
5825 //function : ExtrusionSweep
5827 //=======================================================================
5829 SMESH_MeshEditor::PGroupIDs
5830 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5831 ExtrusParam& theParams,
5832 TTElemOfElemListMap& newElemsMap)
5836 setElemsFirst( theElemSets );
5837 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5838 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5840 // source elements for each generated one
5841 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5842 srcElems.reserve( theElemSets[0].size() );
5843 srcNodes.reserve( theElemSets[1].size() );
5845 const int nbSteps = theParams.NbSteps();
5846 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5848 TNodeOfNodeListMap mapNewNodes;
5849 TElemOfVecOfNnlmiMap mapElemNewNodes;
5851 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5852 myMesh->NbFaces(ORDER_QUADRATIC) +
5853 myMesh->NbVolumes(ORDER_QUADRATIC) );
5855 TIDSortedElemSet::iterator itElem;
5856 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5858 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5859 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5861 // check element type
5862 const SMDS_MeshElement* elem = *itElem;
5863 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5866 const size_t nbNodes = elem->NbNodes();
5867 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5868 newNodesItVec.reserve( nbNodes );
5870 // loop on elem nodes
5871 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5872 while ( itN->more() )
5874 // check if a node has been already sweeped
5875 const SMDS_MeshNode* node = itN->next();
5876 TNodeOfNodeListMap::iterator nIt =
5877 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5878 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5879 if ( listNewNodes.empty() )
5883 // check if we are to create medium nodes between corner ones
5884 bool needMediumNodes = false;
5885 if ( isQuadraticMesh )
5887 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5888 while (it->more() && !needMediumNodes )
5890 const SMDS_MeshElement* invElem = it->next();
5891 if ( invElem != elem && !theElems.count( invElem )) continue;
5892 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5893 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5894 needMediumNodes = true;
5897 // create nodes for all steps
5898 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5900 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5901 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5903 myLastCreatedNodes.push_back( *newNodesIt );
5904 srcNodes.push_back( node );
5909 if ( theParams.ToMakeBoundary() )
5911 GetMeshDS()->Modified();
5912 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5914 break; // newNodesItVec will be shorter than nbNodes
5917 newNodesItVec.push_back( nIt );
5919 // make new elements
5920 if ( newNodesItVec.size() == nbNodes )
5921 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5925 if ( theParams.ToMakeBoundary() ) {
5926 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5928 PGroupIDs newGroupIDs;
5929 if ( theParams.ToMakeGroups() )
5930 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5935 //=======================================================================
5936 //function : ExtrusionAlongTrack
5938 //=======================================================================
5939 SMESH_MeshEditor::Extrusion_Error
5940 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5941 SMESH_Mesh* theTrackMesh,
5942 SMDS_ElemIteratorPtr theTrackIterator,
5943 const SMDS_MeshNode* theN1,
5944 std::list<double>& theAngles,
5945 const bool theAngleVariation,
5946 std::list<double>& theScales,
5947 const bool theScaleVariation,
5948 const gp_Pnt* theRefPoint,
5949 const bool theMakeGroups)
5954 if ( theElements[0].empty() && theElements[1].empty() )
5955 return EXTR_NO_ELEMENTS;
5957 ASSERT( theTrackMesh );
5958 if ( ! theTrackIterator || !theTrackIterator->more() )
5959 return EXTR_NO_ELEMENTS;
5961 // 2. Get ordered nodes
5962 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5963 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5964 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5965 if ( branchEdges.empty() )
5966 return EXTR_PATH_NOT_EDGE;
5968 if ( branchEdges.size() > 1 )
5969 return EXTR_BAD_PATH_SHAPE;
5971 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5972 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5973 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5974 return EXTR_BAD_STARTING_NODE;
5976 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5978 // add medium nodes to pathNodes
5979 std::vector< const SMDS_MeshNode* > pathNodes2;
5980 std::vector< const SMDS_MeshElement* > pathEdges2;
5981 pathNodes2.reserve( pathNodes.size() * 2 );
5982 pathEdges2.reserve( pathEdges.size() * 2 );
5983 for ( size_t i = 0; i < pathEdges.size(); ++i )
5985 pathNodes2.push_back( pathNodes[i] );
5986 pathEdges2.push_back( pathEdges[i] );
5987 if ( pathEdges[i]->IsQuadratic() )
5989 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5990 pathEdges2.push_back( pathEdges[i] );
5993 pathNodes2.push_back( pathNodes.back() );
5994 pathEdges.swap( pathEdges2 );
5995 pathNodes.swap( pathNodes2 );
5998 // 3. Get path data at pathNodes
6000 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6002 if ( theAngleVariation )
6003 linearAngleVariation( points.size()-1, theAngles );
6004 if ( theScaleVariation )
6005 linearScaleVariation( points.size()-1, theScales );
6007 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6008 std::list<double>::iterator angle = theAngles.begin();
6010 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6012 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6013 std::map< int, double >::iterator id2factor;
6014 SMESH_MesherHelper pathHelper( *theTrackMesh );
6015 gp_Pnt p; gp_Vec tangent;
6016 const double tol2 = gp::Resolution() * gp::Resolution();
6018 for ( size_t i = 0; i < pathNodes.size(); ++i )
6020 ExtrusParam::PathPoint & point = points[ i ];
6022 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6024 if ( angle != theAngles.end() )
6025 point.myAngle = *angle++;
6027 tangent.SetCoord( 0,0,0 );
6028 const int shapeID = pathNodes[ i ]->GetShapeID();
6029 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6030 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6031 switch ( shapeType )
6035 TopoDS_Edge edge = TopoDS::Edge( shape );
6036 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6037 if ( id2factor->second == 0 )
6039 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6040 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6042 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6043 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6044 curve->D1( u, p, tangent );
6045 tangent *= id2factor->second;
6051 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6052 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6054 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6055 for ( int di = -1; di <= 0; ++di )
6058 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6060 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6061 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6062 if ( id2factor->second == 0 )
6065 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6067 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6069 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6070 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6072 curve->D1( u, p, du );
6073 double size2 = du.SquareMagnitude();
6074 if ( du.SquareMagnitude() > tol2 )
6076 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6089 for ( int di = -1; di <= 1; di += 2 )
6092 if ( j < pathNodes.size() )
6094 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6095 double size2 = dir.SquareMagnitude();
6097 tangent += dir.Divided( Sqrt( size2 )) * di;
6101 } // switch ( shapeType )
6103 if ( tangent.SquareMagnitude() < tol2 )
6104 return EXTR_CANT_GET_TANGENT;
6106 point.myTgt = tangent;
6108 } // loop on pathNodes
6111 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6112 TTElemOfElemListMap newElemsMap;
6114 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6119 //=======================================================================
6120 //function : linearAngleVariation
6121 //purpose : spread values over nbSteps
6122 //=======================================================================
6124 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6125 list<double>& Angles)
6127 int nbAngles = Angles.size();
6128 if( nbSteps > nbAngles && nbAngles > 0 )
6130 vector<double> theAngles(nbAngles);
6131 theAngles.assign( Angles.begin(), Angles.end() );
6134 double rAn2St = double( nbAngles ) / double( nbSteps );
6135 double angPrev = 0, angle;
6136 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6138 double angCur = rAn2St * ( iSt+1 );
6139 double angCurFloor = floor( angCur );
6140 double angPrevFloor = floor( angPrev );
6141 if ( angPrevFloor == angCurFloor )
6142 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6144 int iP = int( angPrevFloor );
6145 double angPrevCeil = ceil(angPrev);
6146 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6148 int iC = int( angCurFloor );
6149 if ( iC < nbAngles )
6150 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6152 iP = int( angPrevCeil );
6154 angle += theAngles[ iC ];
6156 res.push_back(angle);
6163 //=======================================================================
6164 //function : linearScaleVariation
6165 //purpose : spread values over nbSteps
6166 //=======================================================================
6168 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6169 std::list<double>& theScales)
6171 int nbScales = theScales.size();
6172 std::vector<double> myScales;
6173 myScales.reserve( theNbSteps );
6174 std::list<double>::const_iterator scale = theScales.begin();
6175 double prevScale = 1.0;
6176 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6178 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6179 int stDelta = Max( 1, iStep - myScales.size());
6180 double scDelta = ( *scale - prevScale ) / stDelta;
6181 for ( int iStep = 0; iStep < stDelta; ++iStep )
6183 myScales.push_back( prevScale + scDelta );
6184 prevScale = myScales.back();
6188 theScales.assign( myScales.begin(), myScales.end() );
6191 //================================================================================
6193 * \brief Move or copy theElements applying theTrsf to their nodes
6194 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6195 * \param theTrsf - transformation to apply
6196 * \param theCopy - if true, create translated copies of theElems
6197 * \param theMakeGroups - if true and theCopy, create translated groups
6198 * \param theTargetMesh - mesh to copy translated elements into
6199 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6201 //================================================================================
6203 SMESH_MeshEditor::PGroupIDs
6204 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6205 const gp_Trsf& theTrsf,
6207 const bool theMakeGroups,
6208 SMESH_Mesh* theTargetMesh)
6211 myLastCreatedElems.reserve( theElems.size() );
6213 bool needReverse = false;
6214 string groupPostfix;
6215 switch ( theTrsf.Form() ) {
6218 groupPostfix = "mirrored";
6221 groupPostfix = "mirrored";
6225 groupPostfix = "mirrored";
6228 groupPostfix = "rotated";
6230 case gp_Translation:
6231 groupPostfix = "translated";
6234 groupPostfix = "scaled";
6236 case gp_CompoundTrsf: // different scale by axis
6237 groupPostfix = "scaled";
6240 needReverse = false;
6241 groupPostfix = "transformed";
6244 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6245 SMESHDS_Mesh* aMesh = GetMeshDS();
6247 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6248 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6249 SMESH_MeshEditor::ElemFeatures elemType;
6251 // map old node to new one
6252 TNodeNodeMap nodeMap;
6254 // elements sharing moved nodes; those of them which have all
6255 // nodes mirrored but are not in theElems are to be reversed
6256 TIDSortedElemSet inverseElemSet;
6258 // source elements for each generated one
6259 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6261 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6262 TIDSortedElemSet orphanNode;
6264 if ( theElems.empty() ) // transform the whole mesh
6267 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6268 while ( eIt->more() ) theElems.insert( eIt->next() );
6270 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6271 while ( nIt->more() )
6273 const SMDS_MeshNode* node = nIt->next();
6274 if ( node->NbInverseElements() == 0)
6275 orphanNode.insert( node );
6279 // loop on elements to transform nodes : first orphan nodes then elems
6280 TIDSortedElemSet::iterator itElem;
6281 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6282 for (int i=0; i<2; i++)
6283 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6285 const SMDS_MeshElement* elem = *itElem;
6289 // loop on elem nodes
6291 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6292 while ( itN->more() )
6294 const SMDS_MeshNode* node = cast2Node( itN->next() );
6295 // check if a node has been already transformed
6296 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6297 nodeMap.insert( make_pair ( node, node ));
6298 if ( !n2n_isnew.second )
6301 node->GetXYZ( coord );
6302 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6303 if ( theTargetMesh ) {
6304 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6305 n2n_isnew.first->second = newNode;
6306 myLastCreatedNodes.push_back(newNode);
6307 srcNodes.push_back( node );
6309 else if ( theCopy ) {
6310 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6311 n2n_isnew.first->second = newNode;
6312 myLastCreatedNodes.push_back(newNode);
6313 srcNodes.push_back( node );
6316 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6317 // node position on shape becomes invalid
6318 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6319 ( SMDS_SpacePosition::originSpacePosition() );
6322 // keep inverse elements
6323 if ( !theCopy && !theTargetMesh && needReverse ) {
6324 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6325 while ( invElemIt->more() ) {
6326 const SMDS_MeshElement* iel = invElemIt->next();
6327 inverseElemSet.insert( iel );
6331 } // loop on elems in { &orphanNode, &theElems };
6333 // either create new elements or reverse mirrored ones
6334 if ( !theCopy && !needReverse && !theTargetMesh )
6337 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6339 // Replicate or reverse elements
6341 std::vector<int> iForw;
6342 vector<const SMDS_MeshNode*> nodes;
6343 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6345 const SMDS_MeshElement* elem = *itElem;
6346 if ( !elem ) continue;
6348 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6349 size_t nbNodes = elem->NbNodes();
6350 if ( geomType == SMDSGeom_NONE ) continue; // node
6352 nodes.resize( nbNodes );
6354 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6356 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6360 bool allTransformed = true;
6361 int nbFaces = aPolyedre->NbFaces();
6362 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6364 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6365 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6367 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6368 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6369 if ( nodeMapIt == nodeMap.end() )
6370 allTransformed = false; // not all nodes transformed
6372 nodes.push_back((*nodeMapIt).second);
6374 if ( needReverse && allTransformed )
6375 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6377 if ( !allTransformed )
6378 continue; // not all nodes transformed
6380 else // ----------------------- the rest element types
6382 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6383 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6384 const vector<int>& i = needReverse ? iRev : iForw;
6386 // find transformed nodes
6388 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6389 while ( itN->more() ) {
6390 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6391 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6392 if ( nodeMapIt == nodeMap.end() )
6393 break; // not all nodes transformed
6394 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6396 if ( iNode != nbNodes )
6397 continue; // not all nodes transformed
6401 // copy in this or a new mesh
6402 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6403 srcElems.push_back( elem );
6406 // reverse element as it was reversed by transformation
6408 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6411 } // loop on elements
6413 if ( editor && editor != this )
6414 myLastCreatedElems.swap( editor->myLastCreatedElems );
6416 PGroupIDs newGroupIDs;
6418 if ( ( theMakeGroups && theCopy ) ||
6419 ( theMakeGroups && theTargetMesh ) )
6420 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6425 //================================================================================
6427 * \brief Make an offset mesh from a source 2D mesh
6428 * \param [in] theElements - source faces
6429 * \param [in] theValue - offset value
6430 * \param [out] theTgtMesh - a mesh to add offset elements to
6431 * \param [in] theMakeGroups - to generate groups
6432 * \return PGroupIDs - IDs of created groups. NULL means failure
6434 //================================================================================
6436 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6437 const double theValue,
6438 SMESH_Mesh* theTgtMesh,
6439 const bool theMakeGroups,
6440 const bool theCopyElements,
6441 const bool theFixSelfIntersection)
6443 SMESHDS_Mesh* meshDS = GetMeshDS();
6444 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6445 SMESH_MeshEditor tgtEditor( theTgtMesh );
6447 SMDS_ElemIteratorPtr eIt;
6448 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6449 else eIt = SMESHUtils::elemSetIterator( theElements );
6451 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6452 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6453 std::unique_ptr< SMDS_Mesh > offsetMesh
6454 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6455 theFixSelfIntersection,
6456 new2OldFaces, new2OldNodes ));
6457 if ( offsetMesh->NbElements() == 0 )
6458 return PGroupIDs(); // MakeOffset() failed
6461 if ( theTgtMesh == myMesh && !theCopyElements )
6463 // clear the source elements
6464 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6465 else eIt = SMESHUtils::elemSetIterator( theElements );
6466 while ( eIt->more() )
6467 meshDS->RemoveFreeElement( eIt->next(), 0 );
6470 // offsetMesh->Modified();
6471 // offsetMesh->CompactMesh(); // make IDs start from 1
6473 // source elements for each generated one
6474 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6475 srcElems.reserve( new2OldFaces.size() );
6476 srcNodes.reserve( new2OldNodes.size() );
6479 myLastCreatedElems.reserve( new2OldFaces.size() );
6480 myLastCreatedNodes.reserve( new2OldNodes.size() );
6482 // copy offsetMesh to theTgtMesh
6484 int idShift = meshDS->MaxNodeID();
6485 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6486 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6489 if ( n->NbInverseElements() > 0 )
6492 const SMDS_MeshNode* n2 =
6493 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6494 myLastCreatedNodes.push_back( n2 );
6495 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6499 ElemFeatures elemType;
6500 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6501 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6504 elemType.myNodes.clear();
6505 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6507 const SMDS_MeshNode* n2 = nIt->next();
6508 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6510 tgtEditor.AddElement( elemType.myNodes, elemType );
6511 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6514 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6516 PGroupIDs newGroupIDs;
6517 if ( theMakeGroups )
6518 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6520 newGroupIDs.reset( new std::list< int > );
6525 //=======================================================================
6527 * \brief Create groups of elements made during transformation
6528 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6529 * \param elemGens - elements making corresponding myLastCreatedElems
6530 * \param postfix - to push_back to names of new groups
6531 * \param targetMesh - mesh to create groups in
6532 * \param topPresent - is there are "top" elements that are created by sweeping
6534 //=======================================================================
6536 SMESH_MeshEditor::PGroupIDs
6537 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6538 const SMESH_SequenceOfElemPtr& elemGens,
6539 const std::string& postfix,
6540 SMESH_Mesh* targetMesh,
6541 const bool topPresent)
6543 PGroupIDs newGroupIDs( new list<int> );
6544 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6546 // Sort existing groups by types and collect their names
6548 // containers to store an old group and generated new ones;
6549 // 1st new group is for result elems of different type than a source one;
6550 // 2nd new group is for same type result elems ("top" group at extrusion)
6552 using boost::make_tuple;
6553 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6554 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6555 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6557 set< string > groupNames;
6559 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6560 if ( !groupIt->more() ) return newGroupIDs;
6562 int newGroupID = mesh->GetGroupIds().back()+1;
6563 while ( groupIt->more() )
6565 SMESH_Group * group = groupIt->next();
6566 if ( !group ) continue;
6567 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6568 if ( !groupDS || groupDS->IsEmpty() ) continue;
6569 groupNames.insert ( group->GetName() );
6570 groupDS->SetStoreName( group->GetName() );
6571 const SMDSAbs_ElementType type = groupDS->GetType();
6572 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6573 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6574 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6575 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6578 // Loop on nodes and elements to add them in new groups
6580 vector< const SMDS_MeshElement* > resultElems;
6581 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6583 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6584 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6585 if ( gens.size() != elems.size() )
6586 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6588 // loop on created elements
6589 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6591 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6592 if ( !sourceElem ) {
6593 MESSAGE("generateGroups(): NULL source element");
6596 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6597 if ( groupsOldNew.empty() ) { // no groups of this type at all
6598 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6599 ++iElem; // skip all elements made by sourceElem
6602 // collect all elements made by the iElem-th sourceElem
6603 resultElems.clear();
6604 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6605 if ( resElem != sourceElem )
6606 resultElems.push_back( resElem );
6607 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6608 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6609 if ( resElem != sourceElem )
6610 resultElems.push_back( resElem );
6612 const SMDS_MeshElement* topElem = 0;
6613 if ( isNodes ) // there must be a top element
6615 topElem = resultElems.back();
6616 resultElems.pop_back();
6620 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6621 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6622 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6624 topElem = *resElemIt;
6625 *resElemIt = 0; // erase *resElemIt
6629 // add resultElems to groups originted from ones the sourceElem belongs to
6630 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6631 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6633 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6634 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6636 // fill in a new group
6637 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6638 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6639 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6641 newGroup.Add( *resElemIt );
6643 // fill a "top" group
6646 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6647 newTopGroup.Add( topElem );
6651 } // loop on created elements
6652 }// loop on nodes and elements
6654 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6656 list<int> topGrouIds;
6657 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6659 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6660 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6661 orderedOldNewGroups[i]->get<2>() };
6662 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6664 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6665 if ( newGroupDS->IsEmpty() )
6667 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6672 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6675 const bool isTop = ( topPresent &&
6676 newGroupDS->GetType() == oldGroupDS->GetType() &&
6679 string name = oldGroupDS->GetStoreName();
6680 { // remove trailing whitespaces (issue 22599)
6681 size_t size = name.size();
6682 while ( size > 1 && isspace( name[ size-1 ]))
6684 if ( size != name.size() )
6686 name.resize( size );
6687 oldGroupDS->SetStoreName( name.c_str() );
6690 if ( !targetMesh ) {
6691 string suffix = ( isTop ? "top": postfix.c_str() );
6695 while ( !groupNames.insert( name ).second ) // name exists
6696 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6701 newGroupDS->SetStoreName( name.c_str() );
6703 // make a SMESH_Groups
6704 mesh->AddGroup( newGroupDS );
6706 topGrouIds.push_back( newGroupDS->GetID() );
6708 newGroupIDs->push_back( newGroupDS->GetID() );
6712 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6717 //================================================================================
6719 * * \brief Return list of group of nodes close to each other within theTolerance
6720 * * Search among theNodes or in the whole mesh if theNodes is empty using
6721 * * an Octree algorithm
6722 * \param [in,out] theNodes - the nodes to treat
6723 * \param [in] theTolerance - the tolerance
6724 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6725 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6726 * corner and medium nodes in separate groups
6728 //================================================================================
6730 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6731 const double theTolerance,
6732 TListOfListOfNodes & theGroupsOfNodes,
6733 bool theSeparateCornersAndMedium)
6737 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6738 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6739 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6740 theSeparateCornersAndMedium = false;
6742 TIDSortedNodeSet& corners = theNodes;
6743 TIDSortedNodeSet medium;
6745 if ( theNodes.empty() ) // get all nodes in the mesh
6747 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6748 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6749 if ( theSeparateCornersAndMedium )
6750 while ( nIt->more() )
6752 const SMDS_MeshNode* n = nIt->next();
6753 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6754 nodeSet->insert( nodeSet->end(), n );
6757 while ( nIt->more() )
6758 theNodes.insert( theNodes.end(), nIt->next() );
6760 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6762 TIDSortedNodeSet::iterator nIt = corners.begin();
6763 while ( nIt != corners.end() )
6764 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6766 medium.insert( medium.end(), *nIt );
6767 corners.erase( nIt++ );
6775 if ( !corners.empty() )
6776 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6777 if ( !medium.empty() )
6778 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6781 //=======================================================================
6782 //function : SimplifyFace
6783 //purpose : split a chain of nodes into several closed chains
6784 //=======================================================================
6786 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6787 vector<const SMDS_MeshNode *>& poly_nodes,
6788 vector<int>& quantities) const
6790 int nbNodes = faceNodes.size();
6791 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6795 size_t prevNbQuant = quantities.size();
6797 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6798 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6799 map< const SMDS_MeshNode*, int >::iterator nInd;
6801 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6802 simpleNodes.push_back( faceNodes[0] );
6803 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6805 if ( faceNodes[ iCur ] != simpleNodes.back() )
6807 int index = simpleNodes.size();
6808 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6809 int prevIndex = nInd->second;
6810 if ( prevIndex < index )
6813 int loopLen = index - prevIndex;
6816 // store the sub-loop
6817 quantities.push_back( loopLen );
6818 for ( int i = prevIndex; i < index; i++ )
6819 poly_nodes.push_back( simpleNodes[ i ]);
6821 simpleNodes.resize( prevIndex+1 );
6825 simpleNodes.push_back( faceNodes[ iCur ]);
6830 if ( simpleNodes.size() > 2 )
6832 quantities.push_back( simpleNodes.size() );
6833 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6836 return quantities.size() - prevNbQuant;
6839 //=======================================================================
6840 //function : MergeNodes
6841 //purpose : In each group, the cdr of nodes are substituted by the first one
6843 //=======================================================================
6845 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6846 const bool theAvoidMakingHoles)
6850 SMESHDS_Mesh* mesh = GetMeshDS();
6852 TNodeNodeMap nodeNodeMap; // node to replace - new node
6853 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6854 list< int > rmElemIds, rmNodeIds;
6855 vector< ElemFeatures > newElemDefs;
6857 // Fill nodeNodeMap and elems
6859 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6860 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6862 list<const SMDS_MeshNode*>& nodes = *grIt;
6863 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6864 const SMDS_MeshNode* nToKeep = *nIt;
6865 for ( ++nIt; nIt != nodes.end(); nIt++ )
6867 const SMDS_MeshNode* nToRemove = *nIt;
6868 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6869 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6870 while ( invElemIt->more() ) {
6871 const SMDS_MeshElement* elem = invElemIt->next();
6877 // Apply recursive replacements (BUG 0020185)
6878 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6879 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6881 const SMDS_MeshNode* nToKeep = nnIt->second;
6882 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6883 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6885 nToKeep = nnIt_i->second;
6886 nnIt->second = nToKeep;
6887 nnIt_i = nodeNodeMap.find( nToKeep );
6891 if ( theAvoidMakingHoles )
6893 // find elements whose topology changes
6895 vector<const SMDS_MeshElement*> pbElems;
6896 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6897 for ( ; eIt != elems.end(); ++eIt )
6899 const SMDS_MeshElement* elem = *eIt;
6900 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6901 while ( itN->more() )
6903 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6904 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6905 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6907 // several nodes of elem stick
6908 pbElems.push_back( elem );
6913 // exclude from merge nodes causing spoiling element
6914 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6916 bool nodesExcluded = false;
6917 for ( size_t i = 0; i < pbElems.size(); ++i )
6919 size_t prevNbMergeNodes = nodeNodeMap.size();
6920 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6921 prevNbMergeNodes < nodeNodeMap.size() )
6922 nodesExcluded = true;
6924 if ( !nodesExcluded )
6929 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6931 const SMDS_MeshNode* nToRemove = nnIt->first;
6932 const SMDS_MeshNode* nToKeep = nnIt->second;
6933 if ( nToRemove != nToKeep )
6935 rmNodeIds.push_back( nToRemove->GetID() );
6936 AddToSameGroups( nToKeep, nToRemove, mesh );
6937 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6938 // w/o creating node in place of merged ones.
6939 SMDS_PositionPtr pos = nToRemove->GetPosition();
6940 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6941 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6942 sm->SetIsAlwaysComputed( true );
6946 // Change element nodes or remove an element
6948 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6949 for ( ; eIt != elems.end(); eIt++ )
6951 const SMDS_MeshElement* elem = *eIt;
6952 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6954 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6956 rmElemIds.push_back( elem->GetID() );
6958 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6960 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6961 & newElemDefs[i].myNodes[0],
6962 newElemDefs[i].myNodes.size() ))
6966 newElemDefs[i].SetID( elem->GetID() );
6967 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6968 if ( !keepElem ) rmElemIds.pop_back();
6972 newElemDefs[i].SetID( -1 );
6974 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6975 if ( sm && newElem )
6976 sm->AddElement( newElem );
6977 if ( elem != newElem )
6978 ReplaceElemInGroups( elem, newElem, mesh );
6983 // Remove bad elements, then equal nodes (order important)
6984 Remove( rmElemIds, /*isNodes=*/false );
6985 Remove( rmNodeIds, /*isNodes=*/true );
6990 //=======================================================================
6991 //function : applyMerge
6992 //purpose : Compute new connectivity of an element after merging nodes
6993 // \param [in] elems - the element
6994 // \param [out] newElemDefs - definition(s) of result element(s)
6995 // \param [inout] nodeNodeMap - nodes to merge
6996 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6997 // after merging (but not degenerated), removes nodes causing
6998 // the invalidity from \a nodeNodeMap.
6999 // \return bool - true if the element should be removed
7000 //=======================================================================
7002 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7003 vector< ElemFeatures >& newElemDefs,
7004 TNodeNodeMap& nodeNodeMap,
7005 const bool avoidMakingHoles )
7007 bool toRemove = false; // to remove elem
7008 int nbResElems = 1; // nb new elements
7010 newElemDefs.resize(nbResElems);
7011 newElemDefs[0].Init( elem );
7012 newElemDefs[0].myNodes.clear();
7014 set<const SMDS_MeshNode*> nodeSet;
7015 vector< const SMDS_MeshNode*> curNodes;
7016 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7019 const int nbNodes = elem->NbNodes();
7020 SMDSAbs_EntityType entity = elem->GetEntityType();
7022 curNodes.resize( nbNodes );
7023 uniqueNodes.resize( nbNodes );
7024 iRepl.resize( nbNodes );
7025 int iUnique = 0, iCur = 0, nbRepl = 0;
7027 // Get new seq of nodes
7029 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7030 while ( itN->more() )
7032 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7034 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7035 if ( nnIt != nodeNodeMap.end() ) {
7038 curNodes[ iCur ] = n;
7039 bool isUnique = nodeSet.insert( n ).second;
7041 uniqueNodes[ iUnique++ ] = n;
7043 iRepl[ nbRepl++ ] = iCur;
7047 // Analyse element topology after replacement
7049 int nbUniqueNodes = nodeSet.size();
7050 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7055 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7057 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7058 int nbCorners = nbNodes / 2;
7059 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7061 int iNext = ( iCur + 1 ) % nbCorners;
7062 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7064 int iMedium = iCur + nbCorners;
7065 vector< const SMDS_MeshNode* >::iterator i =
7066 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7068 curNodes[ iMedium ]);
7069 if ( i != uniqueNodes.end() )
7072 for ( ; i+1 != uniqueNodes.end(); ++i )
7081 case SMDSEntity_Polygon:
7082 case SMDSEntity_Quad_Polygon: // Polygon
7084 ElemFeatures* elemType = & newElemDefs[0];
7085 const bool isQuad = elemType->myIsQuad;
7087 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7088 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7090 // a polygon can divide into several elements
7091 vector<const SMDS_MeshNode *> polygons_nodes;
7092 vector<int> quantities;
7093 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7094 newElemDefs.resize( nbResElems );
7095 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7097 ElemFeatures* elemType = & newElemDefs[iface];
7098 if ( iface ) elemType->Init( elem );
7100 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7101 int nbNewNodes = quantities[iface];
7102 face_nodes.assign( polygons_nodes.begin() + inode,
7103 polygons_nodes.begin() + inode + nbNewNodes );
7104 inode += nbNewNodes;
7105 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7107 bool isValid = ( nbNewNodes % 2 == 0 );
7108 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7109 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7110 elemType->SetQuad( isValid );
7111 if ( isValid ) // put medium nodes after corners
7112 SMDS_MeshCell::applyInterlaceRev
7113 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7114 nbNewNodes ), face_nodes );
7116 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7118 nbUniqueNodes = newElemDefs[0].myNodes.size();
7122 case SMDSEntity_Polyhedra: // Polyhedral volume
7124 if ( nbUniqueNodes >= 4 )
7126 // each face has to be analyzed in order to check volume validity
7127 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7129 int nbFaces = aPolyedre->NbFaces();
7131 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7132 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7133 vector<const SMDS_MeshNode *> faceNodes;
7137 for (int iface = 1; iface <= nbFaces; iface++)
7139 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7140 faceNodes.resize( nbFaceNodes );
7141 for (int inode = 1; inode <= nbFaceNodes; inode++)
7143 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7144 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7145 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7146 faceNode = (*nnIt).second;
7147 faceNodes[inode - 1] = faceNode;
7149 SimplifyFace(faceNodes, poly_nodes, quantities);
7152 if ( quantities.size() > 3 )
7154 // TODO: remove coincident faces
7156 nbUniqueNodes = newElemDefs[0].myNodes.size();
7164 // TODO not all the possible cases are solved. Find something more generic?
7165 case SMDSEntity_Edge: //////// EDGE
7166 case SMDSEntity_Triangle: //// TRIANGLE
7167 case SMDSEntity_Quad_Triangle:
7168 case SMDSEntity_Tetra:
7169 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7173 case SMDSEntity_Quad_Edge:
7177 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7179 if ( nbUniqueNodes < 3 )
7181 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7182 toRemove = true; // opposite nodes stick
7187 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7196 if ( nbUniqueNodes == 6 &&
7198 ( nbRepl == 1 || iRepl[1] >= 4 ))
7204 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7213 if ( nbUniqueNodes == 7 &&
7215 ( nbRepl == 1 || iRepl[1] != 8 ))
7221 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7223 if ( nbUniqueNodes == 4 ) {
7224 // ---------------------------------> tetrahedron
7225 if ( curNodes[3] == curNodes[4] &&
7226 curNodes[3] == curNodes[5] ) {
7230 else if ( curNodes[0] == curNodes[1] &&
7231 curNodes[0] == curNodes[2] ) {
7232 // bottom nodes stick: set a top before
7233 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7234 uniqueNodes[ 0 ] = curNodes [ 5 ];
7235 uniqueNodes[ 1 ] = curNodes [ 4 ];
7236 uniqueNodes[ 2 ] = curNodes [ 3 ];
7239 else if (( curNodes[0] == curNodes[3] ) +
7240 ( curNodes[1] == curNodes[4] ) +
7241 ( curNodes[2] == curNodes[5] ) == 2 ) {
7242 // a lateral face turns into a line
7246 else if ( nbUniqueNodes == 5 ) {
7247 // PENTAHEDRON --------------------> pyramid
7248 if ( curNodes[0] == curNodes[3] )
7250 uniqueNodes[ 0 ] = curNodes[ 1 ];
7251 uniqueNodes[ 1 ] = curNodes[ 4 ];
7252 uniqueNodes[ 2 ] = curNodes[ 5 ];
7253 uniqueNodes[ 3 ] = curNodes[ 2 ];
7254 uniqueNodes[ 4 ] = curNodes[ 0 ];
7257 if ( curNodes[1] == curNodes[4] )
7259 uniqueNodes[ 0 ] = curNodes[ 0 ];
7260 uniqueNodes[ 1 ] = curNodes[ 2 ];
7261 uniqueNodes[ 2 ] = curNodes[ 5 ];
7262 uniqueNodes[ 3 ] = curNodes[ 3 ];
7263 uniqueNodes[ 4 ] = curNodes[ 1 ];
7266 if ( curNodes[2] == curNodes[5] )
7268 uniqueNodes[ 0 ] = curNodes[ 0 ];
7269 uniqueNodes[ 1 ] = curNodes[ 3 ];
7270 uniqueNodes[ 2 ] = curNodes[ 4 ];
7271 uniqueNodes[ 3 ] = curNodes[ 1 ];
7272 uniqueNodes[ 4 ] = curNodes[ 2 ];
7278 case SMDSEntity_Hexa:
7280 //////////////////////////////////// HEXAHEDRON
7281 SMDS_VolumeTool hexa (elem);
7282 hexa.SetExternalNormal();
7283 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7284 //////////////////////// HEX ---> tetrahedron
7285 for ( int iFace = 0; iFace < 6; iFace++ ) {
7286 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7287 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7288 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7289 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7290 // one face turns into a point ...
7291 int pickInd = ind[ 0 ];
7292 int iOppFace = hexa.GetOppFaceIndex( iFace );
7293 ind = hexa.GetFaceNodesIndices( iOppFace );
7295 uniqueNodes.clear();
7296 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7297 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7300 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7302 if ( nbStick == 1 ) {
7303 // ... and the opposite one - into a triangle.
7305 uniqueNodes.push_back( curNodes[ pickInd ]);
7312 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7313 //////////////////////// HEX ---> prism
7314 int nbTria = 0, iTria[3];
7315 const int *ind; // indices of face nodes
7316 // look for triangular faces
7317 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7318 ind = hexa.GetFaceNodesIndices( iFace );
7319 TIDSortedNodeSet faceNodes;
7320 for ( iCur = 0; iCur < 4; iCur++ )
7321 faceNodes.insert( curNodes[ind[iCur]] );
7322 if ( faceNodes.size() == 3 )
7323 iTria[ nbTria++ ] = iFace;
7325 // check if triangles are opposite
7326 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7328 // set nodes of the bottom triangle
7329 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7331 for ( iCur = 0; iCur < 4; iCur++ )
7332 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7333 indB.push_back( ind[iCur] );
7334 if ( !hexa.IsForward() )
7335 std::swap( indB[0], indB[2] );
7336 for ( iCur = 0; iCur < 3; iCur++ )
7337 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7338 // set nodes of the top triangle
7339 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7340 for ( iCur = 0; iCur < 3; ++iCur )
7341 for ( int j = 0; j < 4; ++j )
7342 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7344 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7351 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7352 //////////////////// HEXAHEDRON ---> pyramid
7353 for ( int iFace = 0; iFace < 6; iFace++ ) {
7354 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7355 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7356 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7357 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7358 // one face turns into a point ...
7359 int iOppFace = hexa.GetOppFaceIndex( iFace );
7360 ind = hexa.GetFaceNodesIndices( iOppFace );
7361 uniqueNodes.clear();
7362 for ( iCur = 0; iCur < 4; iCur++ ) {
7363 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7366 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7368 if ( uniqueNodes.size() == 4 ) {
7369 // ... and the opposite one is a quadrangle
7371 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7372 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7380 if ( toRemove && nbUniqueNodes > 4 ) {
7381 ////////////////// HEXAHEDRON ---> polyhedron
7382 hexa.SetExternalNormal();
7383 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7384 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7385 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7386 quantities.reserve( 6 ); quantities.clear();
7387 for ( int iFace = 0; iFace < 6; iFace++ )
7389 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7390 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7391 curNodes[ind[1]] == curNodes[ind[3]] )
7394 break; // opposite nodes stick
7397 for ( iCur = 0; iCur < 4; iCur++ )
7399 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7400 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7402 if ( nodeSet.size() < 3 )
7403 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7405 quantities.push_back( nodeSet.size() );
7407 if ( quantities.size() >= 4 )
7410 nbUniqueNodes = poly_nodes.size();
7411 newElemDefs[0].SetPoly(true);
7415 } // case HEXAHEDRON
7420 } // switch ( entity )
7422 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7424 // erase from nodeNodeMap nodes whose merge spoils elem
7425 vector< const SMDS_MeshNode* > noMergeNodes;
7426 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7427 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7428 nodeNodeMap.erase( noMergeNodes[i] );
7431 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7433 uniqueNodes.resize( nbUniqueNodes );
7435 if ( !toRemove && nbResElems == 0 )
7438 newElemDefs.resize( nbResElems );
7444 // ========================================================
7445 // class : ComparableElement
7446 // purpose : allow comparing elements basing on their nodes
7447 // ========================================================
7449 class ComparableElement : public boost::container::flat_set< int >
7451 typedef boost::container::flat_set< int > int_set;
7453 const SMDS_MeshElement* myElem;
7455 mutable int myGroupID;
7459 ComparableElement( const SMDS_MeshElement* theElem ):
7460 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7462 this->reserve( theElem->NbNodes() );
7463 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7465 int id = nodeIt->next()->GetID();
7471 const SMDS_MeshElement* GetElem() const { return myElem; }
7473 int& GroupID() const { return myGroupID; }
7474 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7476 ComparableElement( const ComparableElement& theSource ) // move copy
7479 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7480 (int_set&) (*this ) = std::move( src );
7481 myElem = src.myElem;
7482 mySumID = src.mySumID;
7483 myGroupID = src.myGroupID;
7486 static int HashCode(const ComparableElement& se, int limit )
7488 return ::HashCode( se.mySumID, limit );
7490 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7492 return ( se1 == se2 );
7497 //=======================================================================
7498 //function : FindEqualElements
7499 //purpose : Return list of group of elements built on the same nodes.
7500 // Search among theElements or in the whole mesh if theElements is empty
7501 //=======================================================================
7503 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7504 TListOfListOfElementsID & theGroupsOfElementsID )
7508 SMDS_ElemIteratorPtr elemIt;
7509 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7510 else elemIt = SMESHUtils::elemSetIterator( theElements );
7512 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7513 typedef std::list<int> TGroupOfElems;
7514 TMapOfElements mapOfElements;
7515 std::vector< TGroupOfElems > arrayOfGroups;
7516 TGroupOfElems groupOfElems;
7518 while ( elemIt->more() )
7520 const SMDS_MeshElement* curElem = elemIt->next();
7521 if ( curElem->IsNull() )
7523 ComparableElement compElem = curElem;
7525 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7526 if ( elemInSet.GetElem() != curElem ) // coincident elem
7528 int& iG = elemInSet.GroupID();
7531 iG = arrayOfGroups.size();
7532 arrayOfGroups.push_back( groupOfElems );
7533 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7535 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7539 groupOfElems.clear();
7540 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7541 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7543 if ( groupIt->size() > 1 ) {
7544 //groupOfElems.sort(); -- theElements are sorted already
7545 theGroupsOfElementsID.emplace_back( *groupIt );
7550 //=======================================================================
7551 //function : MergeElements
7552 //purpose : In each given group, substitute all elements by the first one.
7553 //=======================================================================
7555 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7559 typedef list<int> TListOfIDs;
7560 TListOfIDs rmElemIds; // IDs of elems to remove
7562 SMESHDS_Mesh* aMesh = GetMeshDS();
7564 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7565 while ( groupsIt != theGroupsOfElementsID.end() ) {
7566 TListOfIDs& aGroupOfElemID = *groupsIt;
7567 aGroupOfElemID.sort();
7568 int elemIDToKeep = aGroupOfElemID.front();
7569 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7570 aGroupOfElemID.pop_front();
7571 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7572 while ( idIt != aGroupOfElemID.end() ) {
7573 int elemIDToRemove = *idIt;
7574 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7575 // add the kept element in groups of removed one (PAL15188)
7576 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7577 rmElemIds.push_back( elemIDToRemove );
7583 Remove( rmElemIds, false );
7586 //=======================================================================
7587 //function : MergeEqualElements
7588 //purpose : Remove all but one of elements built on the same nodes.
7589 //=======================================================================
7591 void SMESH_MeshEditor::MergeEqualElements()
7593 TIDSortedElemSet aMeshElements; /* empty input ==
7594 to merge equal elements in the whole mesh */
7595 TListOfListOfElementsID aGroupsOfElementsID;
7596 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7597 MergeElements( aGroupsOfElementsID );
7600 //=======================================================================
7601 //function : findAdjacentFace
7603 //=======================================================================
7605 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7606 const SMDS_MeshNode* n2,
7607 const SMDS_MeshElement* elem)
7609 TIDSortedElemSet elemSet, avoidSet;
7611 avoidSet.insert ( elem );
7612 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7615 //=======================================================================
7616 //function : findSegment
7617 //purpose : Return a mesh segment by two nodes one of which can be medium
7618 //=======================================================================
7620 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7621 const SMDS_MeshNode* n2)
7623 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7624 while ( it->more() )
7626 const SMDS_MeshElement* seg = it->next();
7627 if ( seg->GetNodeIndex( n2 ) >= 0 )
7633 //=======================================================================
7634 //function : FindFreeBorder
7636 //=======================================================================
7638 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7640 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7641 const SMDS_MeshNode* theSecondNode,
7642 const SMDS_MeshNode* theLastNode,
7643 list< const SMDS_MeshNode* > & theNodes,
7644 list< const SMDS_MeshElement* >& theFaces)
7646 if ( !theFirstNode || !theSecondNode )
7648 // find border face between theFirstNode and theSecondNode
7649 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7653 theFaces.push_back( curElem );
7654 theNodes.push_back( theFirstNode );
7655 theNodes.push_back( theSecondNode );
7657 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7658 //TIDSortedElemSet foundElems;
7659 bool needTheLast = ( theLastNode != 0 );
7661 vector<const SMDS_MeshNode*> nodes;
7663 while ( nStart != theLastNode ) {
7664 if ( nStart == theFirstNode )
7665 return !needTheLast;
7667 // find all free border faces sharing nStart
7669 list< const SMDS_MeshElement* > curElemList;
7670 list< const SMDS_MeshNode* > nStartList;
7671 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7672 while ( invElemIt->more() ) {
7673 const SMDS_MeshElement* e = invElemIt->next();
7674 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7677 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7678 SMDS_MeshElement::iterator() );
7679 nodes.push_back( nodes[ 0 ]);
7682 int iNode = 0, nbNodes = nodes.size() - 1;
7683 for ( iNode = 0; iNode < nbNodes; iNode++ )
7684 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7685 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7686 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7688 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7689 curElemList.push_back( e );
7693 // analyse the found
7695 int nbNewBorders = curElemList.size();
7696 if ( nbNewBorders == 0 ) {
7697 // no free border furthermore
7698 return !needTheLast;
7700 else if ( nbNewBorders == 1 ) {
7701 // one more element found
7703 nStart = nStartList.front();
7704 curElem = curElemList.front();
7705 theFaces.push_back( curElem );
7706 theNodes.push_back( nStart );
7709 // several continuations found
7710 list< const SMDS_MeshElement* >::iterator curElemIt;
7711 list< const SMDS_MeshNode* >::iterator nStartIt;
7712 // check if one of them reached the last node
7713 if ( needTheLast ) {
7714 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7715 curElemIt!= curElemList.end();
7716 curElemIt++, nStartIt++ )
7717 if ( *nStartIt == theLastNode ) {
7718 theFaces.push_back( *curElemIt );
7719 theNodes.push_back( *nStartIt );
7723 // find the best free border by the continuations
7724 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7725 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7726 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7727 curElemIt!= curElemList.end();
7728 curElemIt++, nStartIt++ )
7730 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7731 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7732 // find one more free border
7733 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7737 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7738 // choice: clear a worse one
7739 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7740 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7741 contNodes[ iWorse ].clear();
7742 contFaces[ iWorse ].clear();
7745 if ( contNodes[0].empty() && contNodes[1].empty() )
7748 // push_back the best free border
7749 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7750 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7751 //theNodes.pop_back(); // remove nIgnore
7752 theNodes.pop_back(); // remove nStart
7753 //theFaces.pop_back(); // remove curElem
7754 theNodes.splice( theNodes.end(), *cNL );
7755 theFaces.splice( theFaces.end(), *cFL );
7758 } // several continuations found
7759 } // while ( nStart != theLastNode )
7764 //=======================================================================
7765 //function : CheckFreeBorderNodes
7766 //purpose : Return true if the tree nodes are on a free border
7767 //=======================================================================
7769 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7770 const SMDS_MeshNode* theNode2,
7771 const SMDS_MeshNode* theNode3)
7773 list< const SMDS_MeshNode* > nodes;
7774 list< const SMDS_MeshElement* > faces;
7775 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7778 //=======================================================================
7779 //function : SewFreeBorder
7781 //warning : for border-to-side sewing theSideSecondNode is considered as
7782 // the last side node and theSideThirdNode is not used
7783 //=======================================================================
7785 SMESH_MeshEditor::Sew_Error
7786 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7787 const SMDS_MeshNode* theBordSecondNode,
7788 const SMDS_MeshNode* theBordLastNode,
7789 const SMDS_MeshNode* theSideFirstNode,
7790 const SMDS_MeshNode* theSideSecondNode,
7791 const SMDS_MeshNode* theSideThirdNode,
7792 const bool theSideIsFreeBorder,
7793 const bool toCreatePolygons,
7794 const bool toCreatePolyedrs)
7798 Sew_Error aResult = SEW_OK;
7800 // ====================================
7801 // find side nodes and elements
7802 // ====================================
7804 list< const SMDS_MeshNode* > nSide[ 2 ];
7805 list< const SMDS_MeshElement* > eSide[ 2 ];
7806 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7807 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7811 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7812 nSide[0], eSide[0])) {
7813 MESSAGE(" Free Border 1 not found " );
7814 aResult = SEW_BORDER1_NOT_FOUND;
7816 if (theSideIsFreeBorder) {
7819 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7820 nSide[1], eSide[1])) {
7821 MESSAGE(" Free Border 2 not found " );
7822 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7825 if ( aResult != SEW_OK )
7828 if (!theSideIsFreeBorder) {
7832 // -------------------------------------------------------------------------
7834 // 1. If nodes to merge are not coincident, move nodes of the free border
7835 // from the coord sys defined by the direction from the first to last
7836 // nodes of the border to the correspondent sys of the side 2
7837 // 2. On the side 2, find the links most co-directed with the correspondent
7838 // links of the free border
7839 // -------------------------------------------------------------------------
7841 // 1. Since sewing may break if there are volumes to split on the side 2,
7842 // we won't move nodes but just compute new coordinates for them
7843 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7844 TNodeXYZMap nBordXYZ;
7845 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7846 list< const SMDS_MeshNode* >::iterator nBordIt;
7848 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7849 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7850 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7851 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7852 double tol2 = 1.e-8;
7853 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7854 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7855 // Need node movement.
7857 // find X and Z axes to create trsf
7858 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7860 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7862 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7865 gp_Ax3 toBordAx( Pb1, Zb, X );
7866 gp_Ax3 fromSideAx( Ps1, Zs, X );
7867 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7869 gp_Trsf toBordSys, fromSide2Sys;
7870 toBordSys.SetTransformation( toBordAx );
7871 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7872 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7875 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7876 const SMDS_MeshNode* n = *nBordIt;
7877 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7878 toBordSys.Transforms( xyz );
7879 fromSide2Sys.Transforms( xyz );
7880 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7884 // just insert nodes XYZ in the nBordXYZ map
7885 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7886 const SMDS_MeshNode* n = *nBordIt;
7887 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7891 // 2. On the side 2, find the links most co-directed with the correspondent
7892 // links of the free border
7894 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7895 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7896 sideNodes.push_back( theSideFirstNode );
7898 bool hasVolumes = false;
7899 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7900 set<long> foundSideLinkIDs, checkedLinkIDs;
7901 SMDS_VolumeTool volume;
7902 //const SMDS_MeshNode* faceNodes[ 4 ];
7904 const SMDS_MeshNode* sideNode;
7905 const SMDS_MeshElement* sideElem = 0;
7906 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7907 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7908 nBordIt = bordNodes.begin();
7910 // border node position and border link direction to compare with
7911 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7912 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7913 // choose next side node by link direction or by closeness to
7914 // the current border node:
7915 bool searchByDir = ( *nBordIt != theBordLastNode );
7917 // find the next node on the Side 2
7919 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7921 checkedLinkIDs.clear();
7922 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7924 // loop on inverse elements of current node (prevSideNode) on the Side 2
7925 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7926 while ( invElemIt->more() )
7928 const SMDS_MeshElement* elem = invElemIt->next();
7929 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7930 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7931 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7932 bool isVolume = volume.Set( elem );
7933 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7934 if ( isVolume ) // --volume
7936 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7937 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7938 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7939 while ( nIt->more() ) {
7940 nodes[ iNode ] = cast2Node( nIt->next() );
7941 if ( nodes[ iNode++ ] == prevSideNode )
7942 iPrevNode = iNode - 1;
7944 // there are 2 links to check
7949 // loop on links, to be precise, on the second node of links
7950 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7951 const SMDS_MeshNode* n = nodes[ iNode ];
7953 if ( !volume.IsLinked( n, prevSideNode ))
7957 if ( iNode ) // a node before prevSideNode
7958 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7959 else // a node after prevSideNode
7960 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7962 // check if this link was already used
7963 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7964 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7965 if (!isJustChecked &&
7966 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7968 // test a link geometrically
7969 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7970 bool linkIsBetter = false;
7971 double dot = 0.0, dist = 0.0;
7972 if ( searchByDir ) { // choose most co-directed link
7973 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7974 linkIsBetter = ( dot > maxDot );
7976 else { // choose link with the node closest to bordPos
7977 dist = ( nextXYZ - bordPos ).SquareModulus();
7978 linkIsBetter = ( dist < minDist );
7980 if ( linkIsBetter ) {
7989 } // loop on inverse elements of prevSideNode
7992 MESSAGE(" Can't find path by links of the Side 2 ");
7993 return SEW_BAD_SIDE_NODES;
7995 sideNodes.push_back( sideNode );
7996 sideElems.push_back( sideElem );
7997 foundSideLinkIDs.insert ( linkID );
7998 prevSideNode = sideNode;
8000 if ( *nBordIt == theBordLastNode )
8001 searchByDir = false;
8003 // find the next border link to compare with
8004 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8005 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8006 // move to next border node if sideNode is before forward border node (bordPos)
8007 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8008 prevBordNode = *nBordIt;
8010 bordPos = nBordXYZ[ *nBordIt ];
8011 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8012 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8016 while ( sideNode != theSideSecondNode );
8018 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8019 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8020 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8022 } // end nodes search on the side 2
8024 // ============================
8025 // sew the border to the side 2
8026 // ============================
8028 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8029 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8031 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8032 if ( toMergeConformal && toCreatePolygons )
8034 // do not merge quadrangles if polygons are OK (IPAL0052824)
8035 eIt[0] = eSide[0].begin();
8036 eIt[1] = eSide[1].begin();
8037 bool allQuads[2] = { true, true };
8038 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8039 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8040 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8042 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8045 TListOfListOfNodes nodeGroupsToMerge;
8046 if (( toMergeConformal ) ||
8047 ( theSideIsFreeBorder && !theSideThirdNode )) {
8049 // all nodes are to be merged
8051 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8052 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8053 nIt[0]++, nIt[1]++ )
8055 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8056 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8057 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8062 // insert new nodes into the border and the side to get equal nb of segments
8064 // get normalized parameters of nodes on the borders
8065 vector< double > param[ 2 ];
8066 param[0].resize( maxNbNodes );
8067 param[1].resize( maxNbNodes );
8069 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8070 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8071 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8072 const SMDS_MeshNode* nPrev = *nIt;
8073 double bordLength = 0;
8074 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8075 const SMDS_MeshNode* nCur = *nIt;
8076 gp_XYZ segment (nCur->X() - nPrev->X(),
8077 nCur->Y() - nPrev->Y(),
8078 nCur->Z() - nPrev->Z());
8079 double segmentLen = segment.Modulus();
8080 bordLength += segmentLen;
8081 param[ iBord ][ iNode ] = bordLength;
8084 // normalize within [0,1]
8085 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8086 param[ iBord ][ iNode ] /= bordLength;
8090 // loop on border segments
8091 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8092 int i[ 2 ] = { 0, 0 };
8093 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8094 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8096 // element can be split while iterating on border if it has two edges in the border
8097 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8098 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8100 TElemOfNodeListMap insertMap;
8101 TElemOfNodeListMap::iterator insertMapIt;
8103 // key: elem to insert nodes into
8104 // value: 2 nodes to insert between + nodes to be inserted
8106 bool next[ 2 ] = { false, false };
8108 // find min adjacent segment length after sewing
8109 double nextParam = 10., prevParam = 0;
8110 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8111 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8112 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8113 if ( i[ iBord ] > 0 )
8114 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8116 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8117 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8118 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8120 // choose to insert or to merge nodes
8121 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8122 if ( Abs( du ) <= minSegLen * 0.2 ) {
8125 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8126 const SMDS_MeshNode* n0 = *nIt[0];
8127 const SMDS_MeshNode* n1 = *nIt[1];
8128 nodeGroupsToMerge.back().push_back( n1 );
8129 nodeGroupsToMerge.back().push_back( n0 );
8130 // position of node of the border changes due to merge
8131 param[ 0 ][ i[0] ] += du;
8132 // move n1 for the sake of elem shape evaluation during insertion.
8133 // n1 will be removed by MergeNodes() anyway
8134 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8135 next[0] = next[1] = true;
8140 int intoBord = ( du < 0 ) ? 0 : 1;
8141 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8142 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8143 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8144 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8145 if ( intoBord == 1 ) {
8146 // move node of the border to be on a link of elem of the side
8147 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8148 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8149 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8150 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8152 elemReplaceMapIt = elemReplaceMap.find( elem );
8153 if ( elemReplaceMapIt != elemReplaceMap.end() )
8154 elem = elemReplaceMapIt->second;
8156 insertMapIt = insertMap.find( elem );
8157 bool notFound = ( insertMapIt == insertMap.end() );
8158 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8160 // insert into another link of the same element:
8161 // 1. perform insertion into the other link of the elem
8162 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8163 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8164 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8165 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8166 // 2. perform insertion into the link of adjacent faces
8167 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8168 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8170 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8171 InsertNodesIntoLink( seg, n12, n22, nodeList );
8173 if (toCreatePolyedrs) {
8174 // perform insertion into the links of adjacent volumes
8175 UpdateVolumes(n12, n22, nodeList);
8177 // 3. find an element appeared on n1 and n2 after the insertion
8178 insertMap.erase( insertMapIt );
8179 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8180 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8183 if ( notFound || otherLink ) {
8184 // add element and nodes of the side into the insertMap
8185 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8186 (*insertMapIt).second.push_back( n1 );
8187 (*insertMapIt).second.push_back( n2 );
8189 // add node to be inserted into elem
8190 (*insertMapIt).second.push_back( nIns );
8191 next[ 1 - intoBord ] = true;
8194 // go to the next segment
8195 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8196 if ( next[ iBord ] ) {
8197 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8199 nPrev[ iBord ] = *nIt[ iBord ];
8200 nIt[ iBord ]++; i[ iBord ]++;
8204 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8206 // perform insertion of nodes into elements
8208 for (insertMapIt = insertMap.begin();
8209 insertMapIt != insertMap.end();
8212 const SMDS_MeshElement* elem = (*insertMapIt).first;
8213 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8214 if ( nodeList.size() < 3 ) continue;
8215 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8216 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8218 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8220 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8221 InsertNodesIntoLink( seg, n1, n2, nodeList );
8224 if ( !theSideIsFreeBorder ) {
8225 // look for and insert nodes into the faces adjacent to elem
8226 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8227 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8230 if (toCreatePolyedrs) {
8231 // perform insertion into the links of adjacent volumes
8232 UpdateVolumes(n1, n2, nodeList);
8235 } // end: insert new nodes
8237 MergeNodes ( nodeGroupsToMerge );
8240 // Remove coincident segments
8243 TIDSortedElemSet segments;
8244 SMESH_SequenceOfElemPtr newFaces;
8245 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8247 if ( !myLastCreatedElems[i] ) continue;
8248 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8249 segments.insert( segments.end(), myLastCreatedElems[i] );
8251 newFaces.push_back( myLastCreatedElems[i] );
8253 // get segments adjacent to merged nodes
8254 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8255 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8257 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8258 if ( nodes.front()->IsNull() ) continue;
8259 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8260 while ( segIt->more() )
8261 segments.insert( segIt->next() );
8265 TListOfListOfElementsID equalGroups;
8266 if ( !segments.empty() )
8267 FindEqualElements( segments, equalGroups );
8268 if ( !equalGroups.empty() )
8270 // remove from segments those that will be removed
8271 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8272 for ( ; itGroups != equalGroups.end(); ++itGroups )
8274 list< int >& group = *itGroups;
8275 list< int >::iterator id = group.begin();
8276 for ( ++id; id != group.end(); ++id )
8277 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8278 segments.erase( seg );
8280 // remove equal segments
8281 MergeElements( equalGroups );
8283 // restore myLastCreatedElems
8284 myLastCreatedElems = newFaces;
8285 TIDSortedElemSet::iterator seg = segments.begin();
8286 for ( ; seg != segments.end(); ++seg )
8287 myLastCreatedElems.push_back( *seg );
8293 //=======================================================================
8294 //function : InsertNodesIntoLink
8295 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8296 // and theBetweenNode2 and split theElement
8297 //=======================================================================
8299 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8300 const SMDS_MeshNode* theBetweenNode1,
8301 const SMDS_MeshNode* theBetweenNode2,
8302 list<const SMDS_MeshNode*>& theNodesToInsert,
8303 const bool toCreatePoly)
8305 if ( !theElement ) return;
8307 SMESHDS_Mesh *aMesh = GetMeshDS();
8308 vector<const SMDS_MeshElement*> newElems;
8310 if ( theElement->GetType() == SMDSAbs_Edge )
8312 theNodesToInsert.push_front( theBetweenNode1 );
8313 theNodesToInsert.push_back ( theBetweenNode2 );
8314 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8315 const SMDS_MeshNode* n1 = *n;
8316 for ( ++n; n != theNodesToInsert.end(); ++n )
8318 const SMDS_MeshNode* n2 = *n;
8319 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8320 AddToSameGroups( seg, theElement, aMesh );
8322 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8325 theNodesToInsert.pop_front();
8326 theNodesToInsert.pop_back();
8328 if ( theElement->IsQuadratic() ) // add a not split part
8330 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8331 theElement->end_nodes() );
8332 int iOther = 0, nbN = nodes.size();
8333 for ( ; iOther < nbN; ++iOther )
8334 if ( nodes[iOther] != theBetweenNode1 &&
8335 nodes[iOther] != theBetweenNode2 )
8339 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8340 AddToSameGroups( seg, theElement, aMesh );
8342 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8344 else if ( iOther == 2 )
8346 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8347 AddToSameGroups( seg, theElement, aMesh );
8349 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8352 // treat new elements
8353 for ( size_t i = 0; i < newElems.size(); ++i )
8356 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8357 myLastCreatedElems.push_back( newElems[i] );
8359 ReplaceElemInGroups( theElement, newElems, aMesh );
8360 aMesh->RemoveElement( theElement );
8363 } // if ( theElement->GetType() == SMDSAbs_Edge )
8365 const SMDS_MeshElement* theFace = theElement;
8366 if ( theFace->GetType() != SMDSAbs_Face ) return;
8368 // find indices of 2 link nodes and of the rest nodes
8369 int iNode = 0, il1, il2, i3, i4;
8370 il1 = il2 = i3 = i4 = -1;
8371 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8373 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8374 while ( nodeIt->more() ) {
8375 const SMDS_MeshNode* n = nodeIt->next();
8376 if ( n == theBetweenNode1 )
8378 else if ( n == theBetweenNode2 )
8384 nodes[ iNode++ ] = n;
8386 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8389 // arrange link nodes to go one after another regarding the face orientation
8390 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8391 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8396 aNodesToInsert.reverse();
8398 // check that not link nodes of a quadrangles are in good order
8399 int nbFaceNodes = theFace->NbNodes();
8400 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8406 if (toCreatePoly || theFace->IsPoly()) {
8409 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8411 // add nodes of face up to first node of link
8413 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8414 while ( nodeIt->more() && !isFLN ) {
8415 const SMDS_MeshNode* n = nodeIt->next();
8416 poly_nodes[iNode++] = n;
8417 isFLN = ( n == nodes[il1] );
8419 // add nodes to insert
8420 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8421 for (; nIt != aNodesToInsert.end(); nIt++) {
8422 poly_nodes[iNode++] = *nIt;
8424 // add nodes of face starting from last node of link
8425 while ( nodeIt->more() ) {
8426 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8427 poly_nodes[iNode++] = n;
8431 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8434 else if ( !theFace->IsQuadratic() )
8436 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8437 int nbLinkNodes = 2 + aNodesToInsert.size();
8438 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8439 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8440 linkNodes[ 0 ] = nodes[ il1 ];
8441 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8442 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8443 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8444 linkNodes[ iNode++ ] = *nIt;
8446 // decide how to split a quadrangle: compare possible variants
8447 // and choose which of splits to be a quadrangle
8448 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8449 if ( nbFaceNodes == 3 ) {
8450 iBestQuad = nbSplits;
8453 else if ( nbFaceNodes == 4 ) {
8454 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8455 double aBestRate = DBL_MAX;
8456 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8458 double aBadRate = 0;
8459 // evaluate elements quality
8460 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8461 if ( iSplit == iQuad ) {
8462 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8466 aBadRate += getBadRate( &quad, aCrit );
8469 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8471 nodes[ iSplit < iQuad ? i4 : i3 ]);
8472 aBadRate += getBadRate( &tria, aCrit );
8476 if ( aBadRate < aBestRate ) {
8478 aBestRate = aBadRate;
8483 // create new elements
8485 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8487 if ( iSplit == iBestQuad )
8488 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8493 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8495 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8498 const SMDS_MeshNode* newNodes[ 4 ];
8499 newNodes[ 0 ] = linkNodes[ i1 ];
8500 newNodes[ 1 ] = linkNodes[ i2 ];
8501 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8502 newNodes[ 3 ] = nodes[ i4 ];
8503 if (iSplit == iBestQuad)
8504 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8506 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8508 } // end if(!theFace->IsQuadratic())
8510 else { // theFace is quadratic
8511 // we have to split theFace on simple triangles and one simple quadrangle
8513 int nbshift = tmp*2;
8514 // shift nodes in nodes[] by nbshift
8516 for(i=0; i<nbshift; i++) {
8517 const SMDS_MeshNode* n = nodes[0];
8518 for(j=0; j<nbFaceNodes-1; j++) {
8519 nodes[j] = nodes[j+1];
8521 nodes[nbFaceNodes-1] = n;
8523 il1 = il1 - nbshift;
8524 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8525 // n0 n1 n2 n0 n1 n2
8526 // +-----+-----+ +-----+-----+
8535 // create new elements
8537 if ( nbFaceNodes == 6 ) { // quadratic triangle
8538 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8539 if ( theFace->IsMediumNode(nodes[il1]) ) {
8540 // create quadrangle
8541 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8547 // create quadrangle
8548 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8554 else { // nbFaceNodes==8 - quadratic quadrangle
8555 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8556 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8557 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8558 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8559 // create quadrangle
8560 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8566 // create quadrangle
8567 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8573 // create needed triangles using n1,n2,n3 and inserted nodes
8574 int nbn = 2 + aNodesToInsert.size();
8575 vector<const SMDS_MeshNode*> aNodes(nbn);
8576 aNodes[0 ] = nodes[n1];
8577 aNodes[nbn-1] = nodes[n2];
8578 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8579 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8580 aNodes[iNode++] = *nIt;
8582 for ( i = 1; i < nbn; i++ )
8583 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8586 // remove the old face
8587 for ( size_t i = 0; i < newElems.size(); ++i )
8590 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8591 myLastCreatedElems.push_back( newElems[i] );
8593 ReplaceElemInGroups( theFace, newElems, aMesh );
8594 aMesh->RemoveElement(theFace);
8596 } // InsertNodesIntoLink()
8598 //=======================================================================
8599 //function : UpdateVolumes
8601 //=======================================================================
8603 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8604 const SMDS_MeshNode* theBetweenNode2,
8605 list<const SMDS_MeshNode*>& theNodesToInsert)
8609 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8610 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8611 const SMDS_MeshElement* elem = invElemIt->next();
8613 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8614 SMDS_VolumeTool aVolume (elem);
8615 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8618 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8619 int iface, nbFaces = aVolume.NbFaces();
8620 vector<const SMDS_MeshNode *> poly_nodes;
8621 vector<int> quantities (nbFaces);
8623 for (iface = 0; iface < nbFaces; iface++) {
8624 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8625 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8626 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8628 for (int inode = 0; inode < nbFaceNodes; inode++) {
8629 poly_nodes.push_back(faceNodes[inode]);
8631 if (nbInserted == 0) {
8632 if (faceNodes[inode] == theBetweenNode1) {
8633 if (faceNodes[inode + 1] == theBetweenNode2) {
8634 nbInserted = theNodesToInsert.size();
8636 // add nodes to insert
8637 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8638 for (; nIt != theNodesToInsert.end(); nIt++) {
8639 poly_nodes.push_back(*nIt);
8643 else if (faceNodes[inode] == theBetweenNode2) {
8644 if (faceNodes[inode + 1] == theBetweenNode1) {
8645 nbInserted = theNodesToInsert.size();
8647 // add nodes to insert in reversed order
8648 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8650 for (; nIt != theNodesToInsert.begin(); nIt--) {
8651 poly_nodes.push_back(*nIt);
8653 poly_nodes.push_back(*nIt);
8660 quantities[iface] = nbFaceNodes + nbInserted;
8663 // Replace the volume
8664 SMESHDS_Mesh *aMesh = GetMeshDS();
8666 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8668 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8669 myLastCreatedElems.push_back( newElem );
8670 ReplaceElemInGroups( elem, newElem, aMesh );
8672 aMesh->RemoveElement( elem );
8678 //================================================================================
8680 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8682 //================================================================================
8684 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8685 vector<const SMDS_MeshNode *> & nodes,
8686 vector<int> & nbNodeInFaces )
8689 nbNodeInFaces.clear();
8690 SMDS_VolumeTool vTool ( elem );
8691 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8693 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8694 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8695 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8700 //=======================================================================
8702 * \brief Convert elements contained in a sub-mesh to quadratic
8703 * \return int - nb of checked elements
8705 //=======================================================================
8707 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8708 SMESH_MesherHelper& theHelper,
8709 const bool theForce3d)
8711 //MESSAGE("convertElemToQuadratic");
8713 if( !theSm ) return nbElem;
8715 vector<int> nbNodeInFaces;
8716 vector<const SMDS_MeshNode *> nodes;
8717 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8718 while(ElemItr->more())
8721 const SMDS_MeshElement* elem = ElemItr->next();
8722 if( !elem ) continue;
8724 // analyse a necessity of conversion
8725 const SMDSAbs_ElementType aType = elem->GetType();
8726 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8728 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8729 bool hasCentralNodes = false;
8730 if ( elem->IsQuadratic() )
8733 switch ( aGeomType ) {
8734 case SMDSEntity_Quad_Triangle:
8735 case SMDSEntity_Quad_Quadrangle:
8736 case SMDSEntity_Quad_Hexa:
8737 case SMDSEntity_Quad_Penta:
8738 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8740 case SMDSEntity_BiQuad_Triangle:
8741 case SMDSEntity_BiQuad_Quadrangle:
8742 case SMDSEntity_TriQuad_Hexa:
8743 case SMDSEntity_BiQuad_Penta:
8744 alreadyOK = theHelper.GetIsBiQuadratic();
8745 hasCentralNodes = true;
8750 // take into account already present medium nodes
8752 case SMDSAbs_Volume:
8753 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8755 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8757 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8763 // get elem data needed to re-create it
8765 const int id = elem->GetID();
8766 const int nbNodes = elem->NbCornerNodes();
8767 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8768 if ( aGeomType == SMDSEntity_Polyhedra )
8769 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8770 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8771 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8773 // remove a linear element
8774 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8776 // remove central nodes of biquadratic elements (biquad->quad conversion)
8777 if ( hasCentralNodes )
8778 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8779 if ( nodes[i]->NbInverseElements() == 0 )
8780 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8782 const SMDS_MeshElement* NewElem = 0;
8788 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8796 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8799 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8802 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8806 case SMDSAbs_Volume :
8810 case SMDSEntity_Tetra:
8811 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8813 case SMDSEntity_Pyramid:
8814 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8816 case SMDSEntity_Penta:
8817 case SMDSEntity_Quad_Penta:
8818 case SMDSEntity_BiQuad_Penta:
8819 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8821 case SMDSEntity_Hexa:
8822 case SMDSEntity_Quad_Hexa:
8823 case SMDSEntity_TriQuad_Hexa:
8824 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8825 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8827 case SMDSEntity_Hexagonal_Prism:
8829 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8836 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8837 if( NewElem && NewElem->getshapeId() < 1 )
8838 theSm->AddElement( NewElem );
8842 //=======================================================================
8843 //function : ConvertToQuadratic
8845 //=======================================================================
8847 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8849 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8850 SMESHDS_Mesh* meshDS = GetMeshDS();
8852 SMESH_MesherHelper aHelper(*myMesh);
8854 aHelper.SetIsQuadratic( true );
8855 aHelper.SetIsBiQuadratic( theToBiQuad );
8856 aHelper.SetElementsOnShape(true);
8857 aHelper.ToFixNodeParameters( true );
8859 // convert elements assigned to sub-meshes
8860 int nbCheckedElems = 0;
8861 if ( myMesh->HasShapeToMesh() )
8863 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8865 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8866 while ( smIt->more() ) {
8867 SMESH_subMesh* sm = smIt->next();
8868 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8869 aHelper.SetSubShape( sm->GetSubShape() );
8870 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8876 // convert elements NOT assigned to sub-meshes
8877 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8878 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8880 aHelper.SetElementsOnShape(false);
8881 SMESHDS_SubMesh *smDS = 0;
8884 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8885 while( aEdgeItr->more() )
8887 const SMDS_MeshEdge* edge = aEdgeItr->next();
8888 if ( !edge->IsQuadratic() )
8890 int id = edge->GetID();
8891 const SMDS_MeshNode* n1 = edge->GetNode(0);
8892 const SMDS_MeshNode* n2 = edge->GetNode(1);
8894 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8896 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8897 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8901 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8906 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8907 while( aFaceItr->more() )
8909 const SMDS_MeshFace* face = aFaceItr->next();
8910 if ( !face ) continue;
8912 const SMDSAbs_EntityType type = face->GetEntityType();
8916 case SMDSEntity_Quad_Triangle:
8917 case SMDSEntity_Quad_Quadrangle:
8918 alreadyOK = !theToBiQuad;
8919 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8921 case SMDSEntity_BiQuad_Triangle:
8922 case SMDSEntity_BiQuad_Quadrangle:
8923 alreadyOK = theToBiQuad;
8924 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8926 default: alreadyOK = false;
8931 const int id = face->GetID();
8932 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8934 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8936 SMDS_MeshFace * NewFace = 0;
8939 case SMDSEntity_Triangle:
8940 case SMDSEntity_Quad_Triangle:
8941 case SMDSEntity_BiQuad_Triangle:
8942 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8943 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8944 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8947 case SMDSEntity_Quadrangle:
8948 case SMDSEntity_Quad_Quadrangle:
8949 case SMDSEntity_BiQuad_Quadrangle:
8950 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8951 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8952 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8956 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8958 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8962 vector<int> nbNodeInFaces;
8963 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8964 while(aVolumeItr->more())
8966 const SMDS_MeshVolume* volume = aVolumeItr->next();
8967 if ( !volume ) continue;
8969 const SMDSAbs_EntityType type = volume->GetEntityType();
8970 if ( volume->IsQuadratic() )
8975 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8976 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8977 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8978 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8979 default: alreadyOK = true;
8983 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8987 const int id = volume->GetID();
8988 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8989 if ( type == SMDSEntity_Polyhedra )
8990 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8991 else if ( type == SMDSEntity_Hexagonal_Prism )
8992 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8994 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8996 SMDS_MeshVolume * NewVolume = 0;
8999 case SMDSEntity_Tetra:
9000 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9002 case SMDSEntity_Hexa:
9003 case SMDSEntity_Quad_Hexa:
9004 case SMDSEntity_TriQuad_Hexa:
9005 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9006 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9007 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9008 if ( nodes[i]->NbInverseElements() == 0 )
9009 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9011 case SMDSEntity_Pyramid:
9012 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9013 nodes[3], nodes[4], id, theForce3d);
9015 case SMDSEntity_Penta:
9016 case SMDSEntity_Quad_Penta:
9017 case SMDSEntity_BiQuad_Penta:
9018 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9019 nodes[3], nodes[4], nodes[5], id, theForce3d);
9020 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9021 if ( nodes[i]->NbInverseElements() == 0 )
9022 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9024 case SMDSEntity_Hexagonal_Prism:
9026 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9028 ReplaceElemInGroups(volume, NewVolume, meshDS);
9033 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9034 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9035 // aHelper.FixQuadraticElements(myError);
9036 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9040 //================================================================================
9042 * \brief Makes given elements quadratic
9043 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9044 * \param theElements - elements to make quadratic
9046 //================================================================================
9048 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9049 TIDSortedElemSet& theElements,
9050 const bool theToBiQuad)
9052 if ( theElements.empty() ) return;
9054 // we believe that all theElements are of the same type
9055 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9057 // get all nodes shared by theElements
9058 TIDSortedNodeSet allNodes;
9059 TIDSortedElemSet::iterator eIt = theElements.begin();
9060 for ( ; eIt != theElements.end(); ++eIt )
9061 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9063 // complete theElements with elements of lower dim whose all nodes are in allNodes
9065 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9066 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9067 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9068 for ( ; nIt != allNodes.end(); ++nIt )
9070 const SMDS_MeshNode* n = *nIt;
9071 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9072 while ( invIt->more() )
9074 const SMDS_MeshElement* e = invIt->next();
9075 const SMDSAbs_ElementType type = e->GetType();
9076 if ( e->IsQuadratic() )
9078 quadAdjacentElems[ type ].insert( e );
9081 switch ( e->GetEntityType() ) {
9082 case SMDSEntity_Quad_Triangle:
9083 case SMDSEntity_Quad_Quadrangle:
9084 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9085 case SMDSEntity_BiQuad_Triangle:
9086 case SMDSEntity_BiQuad_Quadrangle:
9087 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9088 default: alreadyOK = true;
9093 if ( type >= elemType )
9094 continue; // same type or more complex linear element
9096 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9097 continue; // e is already checked
9101 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9102 while ( nodeIt->more() && allIn )
9103 allIn = allNodes.count( nodeIt->next() );
9105 theElements.insert(e );
9109 SMESH_MesherHelper helper(*myMesh);
9110 helper.SetIsQuadratic( true );
9111 helper.SetIsBiQuadratic( theToBiQuad );
9113 // add links of quadratic adjacent elements to the helper
9115 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9116 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9117 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9119 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9121 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9122 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9123 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9125 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9127 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9128 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9129 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9131 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9134 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9136 SMESHDS_Mesh* meshDS = GetMeshDS();
9137 SMESHDS_SubMesh* smDS = 0;
9138 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9140 const SMDS_MeshElement* elem = *eIt;
9143 int nbCentralNodes = 0;
9144 switch ( elem->GetEntityType() ) {
9145 // linear convertible
9146 case SMDSEntity_Edge:
9147 case SMDSEntity_Triangle:
9148 case SMDSEntity_Quadrangle:
9149 case SMDSEntity_Tetra:
9150 case SMDSEntity_Pyramid:
9151 case SMDSEntity_Hexa:
9152 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9153 // quadratic that can become bi-quadratic
9154 case SMDSEntity_Quad_Triangle:
9155 case SMDSEntity_Quad_Quadrangle:
9156 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9158 case SMDSEntity_BiQuad_Triangle:
9159 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9160 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9162 default: alreadyOK = true;
9164 if ( alreadyOK ) continue;
9166 const SMDSAbs_ElementType type = elem->GetType();
9167 const int id = elem->GetID();
9168 const int nbNodes = elem->NbCornerNodes();
9169 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9171 helper.SetSubShape( elem->getshapeId() );
9173 if ( !smDS || !smDS->Contains( elem ))
9174 smDS = meshDS->MeshElements( elem->getshapeId() );
9175 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9177 SMDS_MeshElement * newElem = 0;
9180 case 4: // cases for most frequently used element types go first (for optimization)
9181 if ( type == SMDSAbs_Volume )
9182 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9184 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9187 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9188 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9191 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9194 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9197 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9198 nodes[4], id, theForce3d);
9201 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9202 nodes[4], nodes[5], id, theForce3d);
9206 ReplaceElemInGroups( elem, newElem, meshDS);
9207 if( newElem && smDS )
9208 smDS->AddElement( newElem );
9210 // remove central nodes
9211 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9212 if ( nodes[i]->NbInverseElements() == 0 )
9213 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9215 } // loop on theElements
9218 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9219 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9220 // helper.FixQuadraticElements( myError );
9221 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9225 //=======================================================================
9227 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9228 * \return int - nb of checked elements
9230 //=======================================================================
9232 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9233 SMDS_ElemIteratorPtr theItr,
9234 const int /*theShapeID*/)
9237 SMESHDS_Mesh* meshDS = GetMeshDS();
9238 ElemFeatures elemType;
9239 vector<const SMDS_MeshNode *> nodes;
9241 while( theItr->more() )
9243 const SMDS_MeshElement* elem = theItr->next();
9245 if( elem && elem->IsQuadratic())
9248 int nbCornerNodes = elem->NbCornerNodes();
9249 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9251 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9253 //remove a quadratic element
9254 if ( !theSm || !theSm->Contains( elem ))
9255 theSm = meshDS->MeshElements( elem->getshapeId() );
9256 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9258 // remove medium nodes
9259 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9260 if ( nodes[i]->NbInverseElements() == 0 )
9261 meshDS->RemoveFreeNode( nodes[i], theSm );
9263 // add a linear element
9264 nodes.resize( nbCornerNodes );
9265 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9266 ReplaceElemInGroups(elem, newElem, meshDS);
9267 if( theSm && newElem )
9268 theSm->AddElement( newElem );
9274 //=======================================================================
9275 //function : ConvertFromQuadratic
9277 //=======================================================================
9279 bool SMESH_MeshEditor::ConvertFromQuadratic()
9281 int nbCheckedElems = 0;
9282 if ( myMesh->HasShapeToMesh() )
9284 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9286 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9287 while ( smIt->more() ) {
9288 SMESH_subMesh* sm = smIt->next();
9289 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9290 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9296 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9297 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9299 SMESHDS_SubMesh *aSM = 0;
9300 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9308 //================================================================================
9310 * \brief Return true if all medium nodes of the element are in the node set
9312 //================================================================================
9314 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9316 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9317 if ( !nodeSet.count( elem->GetNode(i) ))
9323 //================================================================================
9325 * \brief Makes given elements linear
9327 //================================================================================
9329 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9331 if ( theElements.empty() ) return;
9333 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9334 set<int> mediumNodeIDs;
9335 TIDSortedElemSet::iterator eIt = theElements.begin();
9336 for ( ; eIt != theElements.end(); ++eIt )
9338 const SMDS_MeshElement* e = *eIt;
9339 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9340 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9343 // replace given elements by linear ones
9344 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9345 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9347 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9348 // except those elements sharing medium nodes of quadratic element whose medium nodes
9349 // are not all in mediumNodeIDs
9351 // get remaining medium nodes
9352 TIDSortedNodeSet mediumNodes;
9353 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9354 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9355 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9356 mediumNodes.insert( mediumNodes.end(), n );
9358 // find more quadratic elements to convert
9359 TIDSortedElemSet moreElemsToConvert;
9360 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9361 for ( ; nIt != mediumNodes.end(); ++nIt )
9363 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9364 while ( invIt->more() )
9366 const SMDS_MeshElement* e = invIt->next();
9367 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9369 // find a more complex element including e and
9370 // whose medium nodes are not in mediumNodes
9371 bool complexFound = false;
9372 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9374 SMDS_ElemIteratorPtr invIt2 =
9375 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9376 while ( invIt2->more() )
9378 const SMDS_MeshElement* eComplex = invIt2->next();
9379 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9381 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9382 if ( nbCommonNodes == e->NbNodes())
9384 complexFound = true;
9385 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9391 if ( !complexFound )
9392 moreElemsToConvert.insert( e );
9396 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9397 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9400 //=======================================================================
9401 //function : SewSideElements
9403 //=======================================================================
9405 SMESH_MeshEditor::Sew_Error
9406 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9407 TIDSortedElemSet& theSide2,
9408 const SMDS_MeshNode* theFirstNode1,
9409 const SMDS_MeshNode* theFirstNode2,
9410 const SMDS_MeshNode* theSecondNode1,
9411 const SMDS_MeshNode* theSecondNode2)
9415 if ( theSide1.size() != theSide2.size() )
9416 return SEW_DIFF_NB_OF_ELEMENTS;
9418 Sew_Error aResult = SEW_OK;
9420 // 1. Build set of faces representing each side
9421 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9422 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9424 // =======================================================================
9425 // 1. Build set of faces representing each side:
9426 // =======================================================================
9427 // a. build set of nodes belonging to faces
9428 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9429 // c. create temporary faces representing side of volumes if correspondent
9430 // face does not exist
9432 SMESHDS_Mesh* aMesh = GetMeshDS();
9433 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9434 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9435 TIDSortedElemSet faceSet1, faceSet2;
9436 set<const SMDS_MeshElement*> volSet1, volSet2;
9437 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9438 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9439 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9440 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9441 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9442 int iSide, iFace, iNode;
9444 list<const SMDS_MeshElement* > tempFaceList;
9445 for ( iSide = 0; iSide < 2; iSide++ ) {
9446 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9447 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9448 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9449 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9450 set<const SMDS_MeshElement*>::iterator vIt;
9451 TIDSortedElemSet::iterator eIt;
9452 set<const SMDS_MeshNode*>::iterator nIt;
9454 // check that given nodes belong to given elements
9455 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9456 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9457 int firstIndex = -1, secondIndex = -1;
9458 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9459 const SMDS_MeshElement* elem = *eIt;
9460 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9461 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9462 if ( firstIndex > -1 && secondIndex > -1 ) break;
9464 if ( firstIndex < 0 || secondIndex < 0 ) {
9465 // we can simply return until temporary faces created
9466 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9469 // -----------------------------------------------------------
9470 // 1a. Collect nodes of existing faces
9471 // and build set of face nodes in order to detect missing
9472 // faces corresponding to sides of volumes
9473 // -----------------------------------------------------------
9475 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9477 // loop on the given element of a side
9478 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9479 //const SMDS_MeshElement* elem = *eIt;
9480 const SMDS_MeshElement* elem = *eIt;
9481 if ( elem->GetType() == SMDSAbs_Face ) {
9482 faceSet->insert( elem );
9483 set <const SMDS_MeshNode*> faceNodeSet;
9484 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9485 while ( nodeIt->more() ) {
9486 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9487 nodeSet->insert( n );
9488 faceNodeSet.insert( n );
9490 setOfFaceNodeSet.insert( faceNodeSet );
9492 else if ( elem->GetType() == SMDSAbs_Volume )
9493 volSet->insert( elem );
9495 // ------------------------------------------------------------------------------
9496 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9497 // ------------------------------------------------------------------------------
9499 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9500 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9501 while ( fIt->more() ) { // loop on faces sharing a node
9502 const SMDS_MeshElement* f = fIt->next();
9503 if ( faceSet->find( f ) == faceSet->end() ) {
9504 // check if all nodes are in nodeSet and
9505 // complete setOfFaceNodeSet if they are
9506 set <const SMDS_MeshNode*> faceNodeSet;
9507 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9508 bool allInSet = true;
9509 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9510 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9511 if ( nodeSet->find( n ) == nodeSet->end() )
9514 faceNodeSet.insert( n );
9517 faceSet->insert( f );
9518 setOfFaceNodeSet.insert( faceNodeSet );
9524 // -------------------------------------------------------------------------
9525 // 1c. Create temporary faces representing sides of volumes if correspondent
9526 // face does not exist
9527 // -------------------------------------------------------------------------
9529 if ( !volSet->empty() ) {
9530 //int nodeSetSize = nodeSet->size();
9532 // loop on given volumes
9533 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9534 SMDS_VolumeTool vol (*vIt);
9535 // loop on volume faces: find free faces
9536 // --------------------------------------
9537 list<const SMDS_MeshElement* > freeFaceList;
9538 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9539 if ( !vol.IsFreeFace( iFace ))
9541 // check if there is already a face with same nodes in a face set
9542 const SMDS_MeshElement* aFreeFace = 0;
9543 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9544 int nbNodes = vol.NbFaceNodes( iFace );
9545 set <const SMDS_MeshNode*> faceNodeSet;
9546 vol.GetFaceNodes( iFace, faceNodeSet );
9547 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9549 // no such a face is given but it still can exist, check it
9550 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9551 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9554 // create a temporary face
9555 if ( nbNodes == 3 ) {
9556 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9557 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9559 else if ( nbNodes == 4 ) {
9560 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9561 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9564 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9565 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9566 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9569 tempFaceList.push_back( aFreeFace );
9573 freeFaceList.push_back( aFreeFace );
9575 } // loop on faces of a volume
9577 // choose one of several free faces of a volume
9578 // --------------------------------------------
9579 if ( freeFaceList.size() > 1 ) {
9580 // choose a face having max nb of nodes shared by other elems of a side
9581 int maxNbNodes = -1;
9582 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9583 while ( fIt != freeFaceList.end() ) { // loop on free faces
9584 int nbSharedNodes = 0;
9585 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9586 while ( nodeIt->more() ) { // loop on free face nodes
9587 const SMDS_MeshNode* n =
9588 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9589 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9590 while ( invElemIt->more() ) {
9591 const SMDS_MeshElement* e = invElemIt->next();
9592 nbSharedNodes += faceSet->count( e );
9593 nbSharedNodes += elemSet->count( e );
9596 if ( nbSharedNodes > maxNbNodes ) {
9597 maxNbNodes = nbSharedNodes;
9598 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9600 else if ( nbSharedNodes == maxNbNodes ) {
9604 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9607 if ( freeFaceList.size() > 1 )
9609 // could not choose one face, use another way
9610 // choose a face most close to the bary center of the opposite side
9611 gp_XYZ aBC( 0., 0., 0. );
9612 set <const SMDS_MeshNode*> addedNodes;
9613 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9614 eIt = elemSet2->begin();
9615 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9616 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9617 while ( nodeIt->more() ) { // loop on free face nodes
9618 const SMDS_MeshNode* n =
9619 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9620 if ( addedNodes.insert( n ).second )
9621 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9624 aBC /= addedNodes.size();
9625 double minDist = DBL_MAX;
9626 fIt = freeFaceList.begin();
9627 while ( fIt != freeFaceList.end() ) { // loop on free faces
9629 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9630 while ( nodeIt->more() ) { // loop on free face nodes
9631 const SMDS_MeshNode* n =
9632 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9633 gp_XYZ p( n->X(),n->Y(),n->Z() );
9634 dist += ( aBC - p ).SquareModulus();
9636 if ( dist < minDist ) {
9638 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9641 fIt = freeFaceList.erase( fIt++ );
9644 } // choose one of several free faces of a volume
9646 if ( freeFaceList.size() == 1 ) {
9647 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9648 faceSet->insert( aFreeFace );
9649 // complete a node set with nodes of a found free face
9650 // for ( iNode = 0; iNode < ; iNode++ )
9651 // nodeSet->insert( fNodes[ iNode ] );
9654 } // loop on volumes of a side
9656 // // complete a set of faces if new nodes in a nodeSet appeared
9657 // // ----------------------------------------------------------
9658 // if ( nodeSetSize != nodeSet->size() ) {
9659 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9660 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9661 // while ( fIt->more() ) { // loop on faces sharing a node
9662 // const SMDS_MeshElement* f = fIt->next();
9663 // if ( faceSet->find( f ) == faceSet->end() ) {
9664 // // check if all nodes are in nodeSet and
9665 // // complete setOfFaceNodeSet if they are
9666 // set <const SMDS_MeshNode*> faceNodeSet;
9667 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9668 // bool allInSet = true;
9669 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9670 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9671 // if ( nodeSet->find( n ) == nodeSet->end() )
9672 // allInSet = false;
9674 // faceNodeSet.insert( n );
9676 // if ( allInSet ) {
9677 // faceSet->insert( f );
9678 // setOfFaceNodeSet.insert( faceNodeSet );
9684 } // Create temporary faces, if there are volumes given
9687 if ( faceSet1.size() != faceSet2.size() ) {
9688 // delete temporary faces: they are in reverseElements of actual nodes
9689 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9690 // while ( tmpFaceIt->more() )
9691 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9692 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9693 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9694 // aMesh->RemoveElement(*tmpFaceIt);
9695 MESSAGE("Diff nb of faces");
9696 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9699 // ============================================================
9700 // 2. Find nodes to merge:
9701 // bind a node to remove to a node to put instead
9702 // ============================================================
9704 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9705 if ( theFirstNode1 != theFirstNode2 )
9706 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9707 if ( theSecondNode1 != theSecondNode2 )
9708 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9710 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9711 set< long > linkIdSet; // links to process
9712 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9714 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9715 list< NLink > linkList[2];
9716 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9717 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9718 // loop on links in linkList; find faces by links and append links
9719 // of the found faces to linkList
9720 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9721 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9723 NLink link[] = { *linkIt[0], *linkIt[1] };
9724 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9725 if ( !linkIdSet.count( linkID ) )
9728 // by links, find faces in the face sets,
9729 // and find indices of link nodes in the found faces;
9730 // in a face set, there is only one or no face sharing a link
9731 // ---------------------------------------------------------------
9733 const SMDS_MeshElement* face[] = { 0, 0 };
9734 vector<const SMDS_MeshNode*> fnodes[2];
9735 int iLinkNode[2][2];
9736 TIDSortedElemSet avoidSet;
9737 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9738 const SMDS_MeshNode* n1 = link[iSide].first;
9739 const SMDS_MeshNode* n2 = link[iSide].second;
9740 //cout << "Side " << iSide << " ";
9741 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9742 // find a face by two link nodes
9743 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9744 *faceSetPtr[ iSide ], avoidSet,
9745 &iLinkNode[iSide][0],
9746 &iLinkNode[iSide][1] );
9749 //cout << " F " << face[ iSide]->GetID() <<endl;
9750 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9751 // put face nodes to fnodes
9752 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9753 fnodes[ iSide ].assign( nIt, nEnd );
9754 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9758 // check similarity of elements of the sides
9759 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9760 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9761 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9762 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9765 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9767 break; // do not return because it's necessary to remove tmp faces
9770 // set nodes to merge
9771 // -------------------
9773 if ( face[0] && face[1] ) {
9774 const int nbNodes = face[0]->NbNodes();
9775 if ( nbNodes != face[1]->NbNodes() ) {
9776 MESSAGE("Diff nb of face nodes");
9777 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9778 break; // do not return because it s necessary to remove tmp faces
9780 bool reverse[] = { false, false }; // order of nodes in the link
9781 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9782 // analyse link orientation in faces
9783 int i1 = iLinkNode[ iSide ][ 0 ];
9784 int i2 = iLinkNode[ iSide ][ 1 ];
9785 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9787 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9788 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9789 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9791 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9792 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9795 // add other links of the faces to linkList
9796 // -----------------------------------------
9798 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9799 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9800 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9801 if ( !iter_isnew.second ) { // already in a set: no need to process
9802 linkIdSet.erase( iter_isnew.first );
9804 else // new in set == encountered for the first time: add
9806 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9807 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9808 linkList[0].push_back ( NLink( n1, n2 ));
9809 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9814 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9817 } // loop on link lists
9819 if ( aResult == SEW_OK &&
9820 ( //linkIt[0] != linkList[0].end() ||
9821 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9822 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9823 " " << (faceSetPtr[1]->empty()));
9824 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9827 // ====================================================================
9828 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9829 // ====================================================================
9831 // delete temporary faces
9832 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9833 // while ( tmpFaceIt->more() )
9834 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9835 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9836 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9837 aMesh->RemoveElement(*tmpFaceIt);
9839 if ( aResult != SEW_OK)
9842 list< int > nodeIDsToRemove;
9843 vector< const SMDS_MeshNode*> nodes;
9844 ElemFeatures elemType;
9846 // loop on nodes replacement map
9847 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9848 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9849 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9851 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9852 nodeIDsToRemove.push_back( nToRemove->GetID() );
9853 // loop on elements sharing nToRemove
9854 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9855 while ( invElemIt->more() ) {
9856 const SMDS_MeshElement* e = invElemIt->next();
9857 // get a new suite of nodes: make replacement
9858 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9859 nodes.resize( nbNodes );
9860 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9861 while ( nIt->more() ) {
9862 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9863 nnIt = nReplaceMap.find( n );
9864 if ( nnIt != nReplaceMap.end() ) {
9870 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9871 // elemIDsToRemove.push_back( e->GetID() );
9875 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9876 aMesh->RemoveElement( e );
9878 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9880 AddToSameGroups( newElem, e, aMesh );
9881 if ( int aShapeId = e->getshapeId() )
9882 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9888 Remove( nodeIDsToRemove, true );
9893 //================================================================================
9895 * \brief Find corresponding nodes in two sets of faces
9896 * \param theSide1 - first face set
9897 * \param theSide2 - second first face
9898 * \param theFirstNode1 - a boundary node of set 1
9899 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9900 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9901 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9902 * \param nReplaceMap - output map of corresponding nodes
9903 * \return bool - is a success or not
9905 //================================================================================
9908 //#define DEBUG_MATCHING_NODES
9911 SMESH_MeshEditor::Sew_Error
9912 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9913 set<const SMDS_MeshElement*>& theSide2,
9914 const SMDS_MeshNode* theFirstNode1,
9915 const SMDS_MeshNode* theFirstNode2,
9916 const SMDS_MeshNode* theSecondNode1,
9917 const SMDS_MeshNode* theSecondNode2,
9918 TNodeNodeMap & nReplaceMap)
9920 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9922 nReplaceMap.clear();
9923 //if ( theFirstNode1 != theFirstNode2 )
9924 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9925 //if ( theSecondNode1 != theSecondNode2 )
9926 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9928 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9929 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9931 list< NLink > linkList[2];
9932 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9933 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9935 // loop on links in linkList; find faces by links and append links
9936 // of the found faces to linkList
9937 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9938 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9939 NLink link[] = { *linkIt[0], *linkIt[1] };
9940 if ( linkSet.find( link[0] ) == linkSet.end() )
9943 // by links, find faces in the face sets,
9944 // and find indices of link nodes in the found faces;
9945 // in a face set, there is only one or no face sharing a link
9946 // ---------------------------------------------------------------
9948 const SMDS_MeshElement* face[] = { 0, 0 };
9949 list<const SMDS_MeshNode*> notLinkNodes[2];
9950 //bool reverse[] = { false, false }; // order of notLinkNodes
9952 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9954 const SMDS_MeshNode* n1 = link[iSide].first;
9955 const SMDS_MeshNode* n2 = link[iSide].second;
9956 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9957 set< const SMDS_MeshElement* > facesOfNode1;
9958 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9960 // during a loop of the first node, we find all faces around n1,
9961 // during a loop of the second node, we find one face sharing both n1 and n2
9962 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9963 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9964 while ( fIt->more() ) { // loop on faces sharing a node
9965 const SMDS_MeshElement* f = fIt->next();
9966 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9967 ! facesOfNode1.insert( f ).second ) // f encounters twice
9969 if ( face[ iSide ] ) {
9970 MESSAGE( "2 faces per link " );
9971 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9974 faceSet->erase( f );
9976 // get not link nodes
9977 int nbN = f->NbNodes();
9978 if ( f->IsQuadratic() )
9980 nbNodes[ iSide ] = nbN;
9981 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9982 int i1 = f->GetNodeIndex( n1 );
9983 int i2 = f->GetNodeIndex( n2 );
9984 int iEnd = nbN, iBeg = -1, iDelta = 1;
9985 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9987 std::swap( iEnd, iBeg ); iDelta = -1;
9992 if ( i == iEnd ) i = iBeg + iDelta;
9993 if ( i == i1 ) break;
9994 nodes.push_back ( f->GetNode( i ) );
10000 // check similarity of elements of the sides
10001 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10002 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10003 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10004 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10007 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10011 // set nodes to merge
10012 // -------------------
10014 if ( face[0] && face[1] ) {
10015 if ( nbNodes[0] != nbNodes[1] ) {
10016 MESSAGE("Diff nb of face nodes");
10017 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10019 #ifdef DEBUG_MATCHING_NODES
10020 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10021 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10022 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10024 int nbN = nbNodes[0];
10026 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10027 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10028 for ( int i = 0 ; i < nbN - 2; ++i ) {
10029 #ifdef DEBUG_MATCHING_NODES
10030 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10032 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10036 // add other links of the face 1 to linkList
10037 // -----------------------------------------
10039 const SMDS_MeshElement* f0 = face[0];
10040 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10041 for ( int i = 0; i < nbN; i++ )
10043 const SMDS_MeshNode* n2 = f0->GetNode( i );
10044 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10045 linkSet.insert( SMESH_TLink( n1, n2 ));
10046 if ( !iter_isnew.second ) { // already in a set: no need to process
10047 linkSet.erase( iter_isnew.first );
10049 else // new in set == encountered for the first time: add
10051 #ifdef DEBUG_MATCHING_NODES
10052 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10053 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10055 linkList[0].push_back ( NLink( n1, n2 ));
10056 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10061 } // loop on link lists
10066 namespace // automatically find theAffectedElems for DoubleNodes()
10068 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10070 //--------------------------------------------------------------------------------
10071 // Nodes shared by adjacent FissureBorder's.
10072 // 1 node if FissureBorder separates faces
10073 // 2 nodes if FissureBorder separates volumes
10076 const SMDS_MeshNode* _nodes[2];
10079 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10083 _nbNodes = bool( n1 ) + bool( n2 );
10084 if ( _nbNodes == 2 && n1 > n2 )
10085 std::swap( _nodes[0], _nodes[1] );
10087 bool operator<( const SubBorder& other ) const
10089 for ( int i = 0; i < _nbNodes; ++i )
10091 if ( _nodes[i] < other._nodes[i] ) return true;
10092 if ( _nodes[i] > other._nodes[i] ) return false;
10098 //--------------------------------------------------------------------------------
10099 // Map a SubBorder to all FissureBorder it bounds
10100 struct FissureBorder;
10101 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10102 typedef TBorderLinks::iterator TMappedSub;
10104 //--------------------------------------------------------------------------------
10106 * \brief Element border (volume facet or face edge) at a fissure
10108 struct FissureBorder
10110 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10111 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10113 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10114 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10116 FissureBorder( FissureBorder && from ) // move constructor
10118 std::swap( _nodes, from._nodes );
10119 std::swap( _sortedNodes, from._sortedNodes );
10120 _elems[0] = from._elems[0];
10121 _elems[1] = from._elems[1];
10124 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10125 std::vector< const SMDS_MeshElement* > & adjElems)
10126 : _nodes( elemToDuplicate->NbCornerNodes() )
10128 for ( size_t i = 0; i < _nodes.size(); ++i )
10129 _nodes[i] = elemToDuplicate->GetNode( i );
10131 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10132 findAdjacent( type, adjElems );
10135 FissureBorder( const SMDS_MeshNode** nodes,
10136 const size_t nbNodes,
10137 const SMDSAbs_ElementType adjElemsType,
10138 std::vector< const SMDS_MeshElement* > & adjElems)
10139 : _nodes( nodes, nodes + nbNodes )
10141 findAdjacent( adjElemsType, adjElems );
10144 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10145 std::vector< const SMDS_MeshElement* > & adjElems)
10147 _elems[0] = _elems[1] = 0;
10149 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10150 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10151 _elems[i] = adjElems[i];
10154 bool operator<( const FissureBorder& other ) const
10156 return GetSortedNodes() < other.GetSortedNodes();
10159 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10161 if ( _sortedNodes.empty() && !_nodes.empty() )
10163 FissureBorder* me = const_cast<FissureBorder*>( this );
10164 me->_sortedNodes = me->_nodes;
10165 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10167 return _sortedNodes;
10170 size_t NbSub() const
10172 return _nodes.size();
10175 SubBorder Sub(size_t i) const
10177 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10180 void AddSelfTo( TBorderLinks& borderLinks )
10182 _mappedSubs.resize( NbSub() );
10183 for ( size_t i = 0; i < NbSub(); ++i )
10185 TBorderLinks::iterator s2b =
10186 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10187 s2b->second.push_back( this );
10188 _mappedSubs[ i ] = s2b;
10197 const SMDS_MeshElement* GetMarkedElem() const
10199 if ( _nodes.empty() ) return 0; // cleared
10200 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10201 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10205 gp_XYZ GetNorm() const // normal to the border
10208 if ( _nodes.size() == 2 )
10210 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10211 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10213 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10216 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10217 norm = bordDir ^ avgNorm;
10221 SMESH_NodeXYZ p0( _nodes[0] );
10222 SMESH_NodeXYZ p1( _nodes[1] );
10223 SMESH_NodeXYZ p2( _nodes[2] );
10224 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10226 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10232 void ChooseSide() // mark an _elem located at positive side of fissure
10234 _elems[0]->setIsMarked( true );
10235 gp_XYZ norm = GetNorm();
10236 double maxX = norm.Coord(1);
10237 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10238 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10241 _elems[0]->setIsMarked( false );
10242 _elems[1]->setIsMarked( true );
10246 }; // struct FissureBorder
10248 //--------------------------------------------------------------------------------
10250 * \brief Classifier of elements at fissure edge
10252 class FissureNormal
10254 std::vector< gp_XYZ > _normals;
10258 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10261 _normals.reserve(2);
10262 _normals.push_back( bord.GetNorm() );
10263 if ( _normals.size() == 2 )
10264 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10267 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10270 switch ( _normals.size() ) {
10273 isIn = !isOut( n, _normals[0], elem );
10278 bool in1 = !isOut( n, _normals[0], elem );
10279 bool in2 = !isOut( n, _normals[1], elem );
10280 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10287 //================================================================================
10289 * \brief Classify an element by a plane passing through a node
10291 //================================================================================
10293 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10295 SMESH_NodeXYZ p = n;
10297 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10299 SMESH_NodeXYZ pi = elem->GetNode( i );
10300 sumDot += norm * ( pi - p );
10302 return sumDot < -1e-100;
10305 //================================================================================
10307 * \brief Find FissureBorder's by nodes to duplicate
10309 //================================================================================
10311 void findFissureBorders( const TIDSortedElemSet& theNodes,
10312 std::vector< FissureBorder > & theFissureBorders )
10314 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10315 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10317 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10318 if ( n->NbInverseElements( elemType ) == 0 )
10320 elemType = SMDSAbs_Face;
10321 if ( n->NbInverseElements( elemType ) == 0 )
10324 // unmark elements touching the fissure
10325 for ( ; nIt != theNodes.end(); ++nIt )
10326 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10328 // loop on elements touching the fissure to get their borders belonging to the fissure
10329 std::set< FissureBorder > fissureBorders;
10330 std::vector< const SMDS_MeshElement* > adjElems;
10331 std::vector< const SMDS_MeshNode* > nodes;
10332 SMDS_VolumeTool volTool;
10333 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10335 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10336 while ( invIt->more() )
10338 const SMDS_MeshElement* eInv = invIt->next();
10339 if ( eInv->isMarked() ) continue;
10340 eInv->setIsMarked( true );
10342 if ( elemType == SMDSAbs_Volume )
10344 volTool.Set( eInv );
10345 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10346 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10348 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10349 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10351 bool allOnFissure = true;
10352 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10353 if (( allOnFissure = theNodes.count( nn[ iN ])))
10354 nodes.push_back( nn[ iN ]);
10355 if ( allOnFissure )
10356 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10357 elemType, adjElems )));
10360 else // elemType == SMDSAbs_Face
10362 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10363 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10364 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10366 nn[1] = eInv->GetNode( iN );
10367 onFissure1 = theNodes.count( nn[1] );
10368 if ( onFissure0 && onFissure1 )
10369 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10371 onFissure0 = onFissure1;
10377 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10378 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10379 for ( ; bord != fissureBorders.end(); ++bord )
10381 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10384 } // findFissureBorders()
10386 //================================================================================
10388 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10389 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10390 * \param [in] theNodesNot - nodes not to duplicate
10391 * \param [out] theAffectedElems - the found elements
10393 //================================================================================
10395 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10396 TIDSortedElemSet& theAffectedElems)
10398 if ( theElemsOrNodes.empty() ) return;
10400 // find FissureBorder's
10402 std::vector< FissureBorder > fissure;
10403 std::vector< const SMDS_MeshElement* > elemsByFacet;
10405 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10406 if ( (*elIt)->GetType() == SMDSAbs_Node )
10408 findFissureBorders( theElemsOrNodes, fissure );
10412 fissure.reserve( theElemsOrNodes.size() );
10413 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10414 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10416 if ( fissure.empty() )
10419 // fill borderLinks
10421 TBorderLinks borderLinks;
10423 for ( size_t i = 0; i < fissure.size(); ++i )
10425 fissure[i].AddSelfTo( borderLinks );
10428 // get theAffectedElems
10430 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10431 for ( size_t i = 0; i < fissure.size(); ++i )
10432 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10434 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10435 false, /*markElem=*/true );
10438 std::vector<const SMDS_MeshNode *> facetNodes;
10439 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10440 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10442 // choose a side of fissure
10443 fissure[0].ChooseSide();
10444 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10446 size_t nbCheckedBorders = 0;
10447 while ( nbCheckedBorders < fissure.size() )
10449 // find a FissureBorder to treat
10450 FissureBorder* bord = 0;
10451 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10452 if ( fissure[i].GetMarkedElem() )
10453 bord = & fissure[i];
10454 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10455 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10457 bord = & fissure[i];
10458 bord->ChooseSide();
10459 theAffectedElems.insert( bord->GetMarkedElem() );
10461 if ( !bord ) return;
10462 ++nbCheckedBorders;
10464 // treat FissureBorder's linked to bord
10465 fissureNodes.clear();
10466 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10467 for ( size_t i = 0; i < bord->NbSub(); ++i )
10469 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10470 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10471 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10472 const SubBorder& sb = l2b->first;
10473 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10475 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10477 for ( int j = 0; j < sb._nbNodes; ++j )
10478 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10482 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10483 // until an elem adjacent to a neighbour FissureBorder is found
10484 facetNodes.clear();
10485 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10486 facetNodes.resize( sb._nbNodes + 1 );
10490 // check if bordElem is adjacent to a neighbour FissureBorder
10491 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10493 FissureBorder* bord2 = linkedBorders[j];
10494 if ( bord2 == bord ) continue;
10495 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10498 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10503 // find the next bordElem
10504 const SMDS_MeshElement* nextBordElem = 0;
10505 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10507 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10508 if ( fissureNodes.count( n )) continue;
10510 facetNodes[ sb._nbNodes ] = n;
10511 elemsByFacet.clear();
10512 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10514 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10515 if ( elemsByFacet[ iE ] != bordElem &&
10516 !elemsByFacet[ iE ]->isMarked() )
10518 theAffectedElems.insert( elemsByFacet[ iE ]);
10519 elemsByFacet[ iE ]->setIsMarked( true );
10520 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10521 nextBordElem = elemsByFacet[ iE ];
10525 bordElem = nextBordElem;
10527 } // while ( bordElem )
10529 linkedBorders.clear(); // not to treat this link any more
10531 } // loop on SubBorder's of a FissureBorder
10535 } // loop on FissureBorder's
10538 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10540 // mark nodes of theAffectedElems
10541 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10543 // unmark nodes of the fissure
10544 elIt = theElemsOrNodes.begin();
10545 if ( (*elIt)->GetType() == SMDSAbs_Node )
10546 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10548 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10550 std::vector< gp_XYZ > normVec;
10552 // loop on nodes of the fissure, add elements having marked nodes
10553 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10555 const SMDS_MeshElement* e = (*elIt);
10556 if ( e->GetType() != SMDSAbs_Node )
10557 e->setIsMarked( true ); // avoid adding a fissure element
10559 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10561 const SMDS_MeshNode* n = e->GetNode( iN );
10562 if ( fissEdgeNodes2Norm.count( n ))
10565 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10566 while ( invIt->more() )
10568 const SMDS_MeshElement* eInv = invIt->next();
10569 if ( eInv->isMarked() ) continue;
10570 eInv->setIsMarked( true );
10572 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10573 while( nIt->more() )
10574 if ( nIt->next()->isMarked())
10576 theAffectedElems.insert( eInv );
10577 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10578 n->setIsMarked( false );
10585 // add elements on the fissure edge
10586 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10587 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10589 const SMDS_MeshNode* edgeNode = n2N->first;
10590 const FissureNormal & normals = n2N->second;
10592 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10593 while ( invIt->more() )
10595 const SMDS_MeshElement* eInv = invIt->next();
10596 if ( eInv->isMarked() ) continue;
10597 eInv->setIsMarked( true );
10599 // classify eInv using normals
10600 bool toAdd = normals.IsIn( edgeNode, eInv );
10601 if ( toAdd ) // check if all nodes lie on the fissure edge
10603 bool notOnEdge = false;
10604 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10605 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10610 theAffectedElems.insert( eInv );
10616 } // findAffectedElems()
10619 //================================================================================
10621 * \brief Create elements equal (on same nodes) to given ones
10622 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10623 * elements of the uppest dimension are duplicated.
10625 //================================================================================
10627 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10629 ClearLastCreated();
10630 SMESHDS_Mesh* mesh = GetMeshDS();
10632 // get an element type and an iterator over elements
10634 SMDSAbs_ElementType type = SMDSAbs_All;
10635 SMDS_ElemIteratorPtr elemIt;
10636 if ( theElements.empty() )
10638 if ( mesh->NbNodes() == 0 )
10640 // get most complex type
10641 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10642 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10643 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10645 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10646 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10649 elemIt = mesh->elementsIterator( type );
10655 //type = (*theElements.begin())->GetType();
10656 elemIt = SMESHUtils::elemSetIterator( theElements );
10659 // un-mark all elements to avoid duplicating just created elements
10660 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10662 // duplicate elements
10664 ElemFeatures elemType;
10666 vector< const SMDS_MeshNode* > nodes;
10667 while ( elemIt->more() )
10669 const SMDS_MeshElement* elem = elemIt->next();
10670 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10671 ( elem->isMarked() ))
10674 elemType.Init( elem, /*basicOnly=*/false );
10675 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10677 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10678 newElem->setIsMarked( true );
10682 //================================================================================
10684 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10685 \param theElems - the list of elements (edges or faces) to be replicated
10686 The nodes for duplication could be found from these elements
10687 \param theNodesNot - list of nodes to NOT replicate
10688 \param theAffectedElems - the list of elements (cells and edges) to which the
10689 replicated nodes should be associated to.
10690 \return TRUE if operation has been completed successfully, FALSE otherwise
10692 //================================================================================
10694 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10695 const TIDSortedElemSet& theNodesNot,
10696 const TIDSortedElemSet& theAffectedElems )
10698 ClearLastCreated();
10700 if ( theElems.size() == 0 )
10703 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10708 TNodeNodeMap anOldNodeToNewNode;
10709 // duplicate elements and nodes
10710 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10711 // replce nodes by duplications
10712 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10716 //================================================================================
10718 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10719 \param theMeshDS - mesh instance
10720 \param theElems - the elements replicated or modified (nodes should be changed)
10721 \param theNodesNot - nodes to NOT replicate
10722 \param theNodeNodeMap - relation of old node to new created node
10723 \param theIsDoubleElem - flag os to replicate element or modify
10724 \return TRUE if operation has been completed successfully, FALSE otherwise
10726 //================================================================================
10728 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10729 const TIDSortedElemSet& theElems,
10730 const TIDSortedElemSet& theNodesNot,
10731 TNodeNodeMap& theNodeNodeMap,
10732 const bool theIsDoubleElem )
10734 // iterate through element and duplicate them (by nodes duplication)
10736 std::vector<const SMDS_MeshNode*> newNodes;
10737 ElemFeatures elemType;
10739 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10740 for ( ; elemItr != theElems.end(); ++elemItr )
10742 const SMDS_MeshElement* anElem = *elemItr;
10746 // duplicate nodes to duplicate element
10747 bool isDuplicate = false;
10748 newNodes.resize( anElem->NbNodes() );
10749 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10751 while ( anIter->more() )
10753 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10754 const SMDS_MeshNode* aNewNode = aCurrNode;
10755 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10756 if ( n2n != theNodeNodeMap.end() )
10758 aNewNode = n2n->second;
10760 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10763 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10764 copyPosition( aCurrNode, aNewNode );
10765 theNodeNodeMap[ aCurrNode ] = aNewNode;
10766 myLastCreatedNodes.push_back( aNewNode );
10768 isDuplicate |= (aCurrNode != aNewNode);
10769 newNodes[ ind++ ] = aNewNode;
10771 if ( !isDuplicate )
10774 if ( theIsDoubleElem )
10775 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10777 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10784 //================================================================================
10786 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10787 \param theNodes - identifiers of nodes to be doubled
10788 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10789 nodes. If list of element identifiers is empty then nodes are doubled but
10790 they not assigned to elements
10791 \return TRUE if operation has been completed successfully, FALSE otherwise
10793 //================================================================================
10795 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10796 const std::list< int >& theListOfModifiedElems )
10798 ClearLastCreated();
10800 if ( theListOfNodes.size() == 0 )
10803 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10807 // iterate through nodes and duplicate them
10809 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10811 std::list< int >::const_iterator aNodeIter;
10812 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10814 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10820 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10823 copyPosition( aNode, aNewNode );
10824 anOldNodeToNewNode[ aNode ] = aNewNode;
10825 myLastCreatedNodes.push_back( aNewNode );
10829 // Change nodes of elements
10831 std::vector<const SMDS_MeshNode*> aNodeArr;
10833 std::list< int >::const_iterator anElemIter;
10834 for ( anElemIter = theListOfModifiedElems.begin();
10835 anElemIter != theListOfModifiedElems.end();
10838 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10842 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10843 for( size_t i = 0; i < aNodeArr.size(); ++i )
10845 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10846 anOldNodeToNewNode.find( aNodeArr[ i ]);
10847 if ( n2n != anOldNodeToNewNode.end() )
10848 aNodeArr[ i ] = n2n->second;
10850 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10858 //================================================================================
10860 \brief Check if element located inside shape
10861 \return TRUE if IN or ON shape, FALSE otherwise
10863 //================================================================================
10865 template<class Classifier>
10866 bool isInside(const SMDS_MeshElement* theElem,
10867 Classifier& theClassifier,
10868 const double theTol)
10870 gp_XYZ centerXYZ (0, 0, 0);
10871 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10872 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10874 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10875 theClassifier.Perform(aPnt, theTol);
10876 TopAbs_State aState = theClassifier.State();
10877 return (aState == TopAbs_IN || aState == TopAbs_ON );
10880 //================================================================================
10882 * \brief Classifier of the 3D point on the TopoDS_Face
10883 * with interaface suitable for isInside()
10885 //================================================================================
10887 struct _FaceClassifier
10889 Extrema_ExtPS _extremum;
10890 BRepAdaptor_Surface _surface;
10891 TopAbs_State _state;
10893 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10895 _extremum.Initialize( _surface,
10896 _surface.FirstUParameter(), _surface.LastUParameter(),
10897 _surface.FirstVParameter(), _surface.LastVParameter(),
10898 _surface.Tolerance(), _surface.Tolerance() );
10900 void Perform(const gp_Pnt& aPnt, double theTol)
10903 _state = TopAbs_OUT;
10904 _extremum.Perform(aPnt);
10905 if ( _extremum.IsDone() )
10906 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10907 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10909 TopAbs_State State() const
10916 //================================================================================
10918 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10919 This method is the first step of DoubleNodeElemGroupsInRegion.
10920 \param theElems - list of groups of elements (edges or faces) to be replicated
10921 \param theNodesNot - list of groups of nodes not to replicated
10922 \param theShape - shape to detect affected elements (element which geometric center
10923 located on or inside shape). If the shape is null, detection is done on faces orientations
10924 (select elements with a gravity center on the side given by faces normals).
10925 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10926 The replicated nodes should be associated to affected elements.
10928 \sa DoubleNodeElemGroupsInRegion()
10930 //================================================================================
10932 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10933 const TIDSortedElemSet& theNodesNot,
10934 const TopoDS_Shape& theShape,
10935 TIDSortedElemSet& theAffectedElems)
10937 if ( theShape.IsNull() )
10939 findAffectedElems( theElems, theAffectedElems );
10943 const double aTol = Precision::Confusion();
10944 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10945 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10946 if ( theShape.ShapeType() == TopAbs_SOLID )
10948 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10949 bsc3d->PerformInfinitePoint(aTol);
10951 else if (theShape.ShapeType() == TopAbs_FACE )
10953 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10956 // iterates on indicated elements and get elements by back references from their nodes
10957 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958 for ( ; elemItr != theElems.end(); ++elemItr )
10960 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10961 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10962 while ( nodeItr->more() )
10964 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10965 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10967 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10968 while ( backElemItr->more() )
10970 const SMDS_MeshElement* curElem = backElemItr->next();
10971 if ( curElem && theElems.find(curElem) == theElems.end() &&
10973 isInside( curElem, *bsc3d, aTol ) :
10974 isInside( curElem, *aFaceClassifier, aTol )))
10975 theAffectedElems.insert( curElem );
10983 //================================================================================
10985 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10986 \param theElems - group of of elements (edges or faces) to be replicated
10987 \param theNodesNot - group of nodes not to replicate
10988 \param theShape - shape to detect affected elements (element which geometric center
10989 located on or inside shape).
10990 The replicated nodes should be associated to affected elements.
10991 \return TRUE if operation has been completed successfully, FALSE otherwise
10993 //================================================================================
10995 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10996 const TIDSortedElemSet& theNodesNot,
10997 const TopoDS_Shape& theShape )
10999 if ( theShape.IsNull() )
11002 const double aTol = Precision::Confusion();
11003 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11004 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11005 if ( theShape.ShapeType() == TopAbs_SOLID )
11007 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11008 bsc3d->PerformInfinitePoint(aTol);
11010 else if (theShape.ShapeType() == TopAbs_FACE )
11012 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11015 // iterates on indicated elements and get elements by back references from their nodes
11016 TIDSortedElemSet anAffected;
11017 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11018 for ( ; elemItr != theElems.end(); ++elemItr )
11020 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11024 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11025 while ( nodeItr->more() )
11027 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11028 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11030 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11031 while ( backElemItr->more() )
11033 const SMDS_MeshElement* curElem = backElemItr->next();
11034 if ( curElem && theElems.find(curElem) == theElems.end() &&
11036 isInside( curElem, *bsc3d, aTol ) :
11037 isInside( curElem, *aFaceClassifier, aTol )))
11038 anAffected.insert( curElem );
11042 return DoubleNodes( theElems, theNodesNot, anAffected );
11046 * \brief compute an oriented angle between two planes defined by four points.
11047 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11048 * @param p0 base of the rotation axe
11049 * @param p1 extremity of the rotation axe
11050 * @param g1 belongs to the first plane
11051 * @param g2 belongs to the second plane
11053 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11055 gp_Vec vref(p0, p1);
11058 gp_Vec n1 = vref.Crossed(v1);
11059 gp_Vec n2 = vref.Crossed(v2);
11061 return n2.AngleWithRef(n1, vref);
11063 catch ( Standard_Failure& ) {
11065 return Max( v1.Magnitude(), v2.Magnitude() );
11069 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11070 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11071 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11072 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11073 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11074 * 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.
11075 * 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.
11076 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11077 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11078 * \param theElems - list of groups of volumes, where a group of volume is a set of
11079 * SMDS_MeshElements sorted by Id.
11080 * \param createJointElems - if TRUE, create the elements
11081 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11082 * the boundary between \a theDomains and the rest mesh
11083 * \return TRUE if operation has been completed successfully, FALSE otherwise
11085 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11086 bool createJointElems,
11087 bool onAllBoundaries)
11089 // MESSAGE("----------------------------------------------");
11090 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11091 // MESSAGE("----------------------------------------------");
11093 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11094 meshDS->BuildDownWardConnectivity(true);
11096 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11098 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11099 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11100 // build the list of nodes shared by 2 or more domains, with their domain indexes
11102 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11103 std::map<int,int>celldom; // cell vtkId --> domain
11104 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11105 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11106 faceDomains.clear();
11108 cellDomains.clear();
11109 nodeDomains.clear();
11110 std::map<int,int> emptyMap;
11111 std::set<int> emptySet;
11114 //MESSAGE(".. Number of domains :"<<theElems.size());
11116 TIDSortedElemSet theRestDomElems;
11117 const int iRestDom = -1;
11118 const int idom0 = onAllBoundaries ? iRestDom : 0;
11119 const int nbDomains = theElems.size();
11121 // Check if the domains do not share an element
11122 for (int idom = 0; idom < nbDomains-1; idom++)
11124 // MESSAGE("... Check of domain #" << idom);
11125 const TIDSortedElemSet& domain = theElems[idom];
11126 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11127 for (; elemItr != domain.end(); ++elemItr)
11129 const SMDS_MeshElement* anElem = *elemItr;
11130 int idombisdeb = idom + 1 ;
11131 // check if the element belongs to a domain further in the list
11132 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11134 const TIDSortedElemSet& domainbis = theElems[idombis];
11135 if ( domainbis.count( anElem ))
11137 MESSAGE(".... Domain #" << idom);
11138 MESSAGE(".... Domain #" << idombis);
11139 throw SALOME_Exception("The domains are not disjoint.");
11146 for (int idom = 0; idom < nbDomains; idom++)
11149 // --- build a map (face to duplicate --> volume to modify)
11150 // with all the faces shared by 2 domains (group of elements)
11151 // and corresponding volume of this domain, for each shared face.
11152 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11154 //MESSAGE("... Neighbors of domain #" << idom);
11155 const TIDSortedElemSet& domain = theElems[idom];
11156 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11157 for (; elemItr != domain.end(); ++elemItr)
11159 const SMDS_MeshElement* anElem = *elemItr;
11162 int vtkId = anElem->GetVtkID();
11163 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11164 int neighborsVtkIds[NBMAXNEIGHBORS];
11165 int downIds[NBMAXNEIGHBORS];
11166 unsigned char downTypes[NBMAXNEIGHBORS];
11167 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11168 for (int n = 0; n < nbNeighbors; n++)
11170 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11171 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11172 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11175 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11177 // MESSAGE("Domain " << idombis);
11178 const TIDSortedElemSet& domainbis = theElems[idombis];
11179 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11181 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11183 DownIdType face(downIds[n], downTypes[n]);
11184 if (!faceDomains[face].count(idom))
11186 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11187 celldom[vtkId] = idom;
11188 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11192 theRestDomElems.insert( elem );
11193 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11194 celldom[neighborsVtkIds[n]] = iRestDom;
11202 //MESSAGE("Number of shared faces " << faceDomains.size());
11203 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11205 // --- explore the shared faces domain by domain,
11206 // explore the nodes of the face and see if they belong to a cell in the domain,
11207 // which has only a node or an edge on the border (not a shared face)
11209 for (int idomain = idom0; idomain < nbDomains; idomain++)
11211 //MESSAGE("Domain " << idomain);
11212 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11213 itface = faceDomains.begin();
11214 for (; itface != faceDomains.end(); ++itface)
11216 const std::map<int, int>& domvol = itface->second;
11217 if (!domvol.count(idomain))
11219 DownIdType face = itface->first;
11220 //MESSAGE(" --- face " << face.cellId);
11221 std::set<int> oldNodes;
11223 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11224 std::set<int>::iterator itn = oldNodes.begin();
11225 for (; itn != oldNodes.end(); ++itn)
11228 //MESSAGE(" node " << oldId);
11229 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11230 for (int i=0; i<l.ncells; i++)
11232 int vtkId = l.cells[i];
11233 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11234 if (!domain.count(anElem))
11236 int vtkType = grid->GetCellType(vtkId);
11237 int downId = grid->CellIdToDownId(vtkId);
11240 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11241 continue; // not OK at this stage of the algorithm:
11242 //no cells created after BuildDownWardConnectivity
11244 DownIdType aCell(downId, vtkType);
11245 cellDomains[aCell][idomain] = vtkId;
11246 celldom[vtkId] = idomain;
11247 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11253 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11254 // for each shared face, get the nodes
11255 // for each node, for each domain of the face, create a clone of the node
11257 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11258 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11259 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11261 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11262 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11263 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11265 //MESSAGE(".. Duplication of the nodes");
11266 for (int idomain = idom0; idomain < nbDomains; idomain++)
11268 itface = faceDomains.begin();
11269 for (; itface != faceDomains.end(); ++itface)
11271 const std::map<int, int>& domvol = itface->second;
11272 if (!domvol.count(idomain))
11274 DownIdType face = itface->first;
11275 //MESSAGE(" --- face " << face.cellId);
11276 std::set<int> oldNodes;
11278 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11279 std::set<int>::iterator itn = oldNodes.begin();
11280 for (; itn != oldNodes.end(); ++itn)
11283 if (nodeDomains[oldId].empty())
11285 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11286 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11288 std::map<int, int>::const_iterator itdom = domvol.begin();
11289 for (; itdom != domvol.end(); ++itdom)
11291 int idom = itdom->first;
11292 //MESSAGE(" domain " << idom);
11293 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11295 if (nodeDomains[oldId].size() >= 2) // a multiple node
11297 vector<int> orderedDoms;
11298 //MESSAGE("multiple node " << oldId);
11299 if (mutipleNodes.count(oldId))
11300 orderedDoms = mutipleNodes[oldId];
11303 map<int,int>::iterator it = nodeDomains[oldId].begin();
11304 for (; it != nodeDomains[oldId].end(); ++it)
11305 orderedDoms.push_back(it->first);
11307 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11308 //stringstream txt;
11309 //for (int i=0; i<orderedDoms.size(); i++)
11310 // txt << orderedDoms[i] << " ";
11311 //MESSAGE("orderedDoms " << txt.str());
11312 mutipleNodes[oldId] = orderedDoms;
11314 double *coords = grid->GetPoint(oldId);
11315 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11316 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11317 int newId = newNode->GetVtkID();
11318 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11319 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11326 //MESSAGE(".. Creation of elements");
11327 for (int idomain = idom0; idomain < nbDomains; idomain++)
11329 itface = faceDomains.begin();
11330 for (; itface != faceDomains.end(); ++itface)
11332 std::map<int, int> domvol = itface->second;
11333 if (!domvol.count(idomain))
11335 DownIdType face = itface->first;
11336 //MESSAGE(" --- face " << face.cellId);
11337 std::set<int> oldNodes;
11339 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11340 int nbMultipleNodes = 0;
11341 std::set<int>::iterator itn = oldNodes.begin();
11342 for (; itn != oldNodes.end(); ++itn)
11345 if (mutipleNodes.count(oldId))
11348 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11350 //MESSAGE("multiple Nodes detected on a shared face");
11351 int downId = itface->first.cellId;
11352 unsigned char cellType = itface->first.cellType;
11353 // --- shared edge or shared face ?
11354 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11357 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11358 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11359 if (mutipleNodes.count(nodes[i]))
11360 if (!mutipleNodesToFace.count(nodes[i]))
11361 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11363 else // shared face (between two volumes)
11365 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11366 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11367 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11368 for (int ie =0; ie < nbEdges; ie++)
11371 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11372 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11374 vector<int> vn0 = mutipleNodes[nodes[0]];
11375 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11377 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11378 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11379 if ( vn0[i0] == vn1[i1] )
11380 doms.push_back( vn0[ i0 ]);
11381 if ( doms.size() > 2 )
11383 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11384 double *coords = grid->GetPoint(nodes[0]);
11385 gp_Pnt p0(coords[0], coords[1], coords[2]);
11386 coords = grid->GetPoint(nodes[nbNodes - 1]);
11387 gp_Pnt p1(coords[0], coords[1], coords[2]);
11389 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11390 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11391 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11392 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11393 for ( size_t id = 0; id < doms.size(); id++ )
11395 int idom = doms[id];
11396 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11397 for ( int ivol = 0; ivol < nbvol; ivol++ )
11399 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11400 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11401 if (domain.count(elem))
11403 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11404 domvol[idom] = (SMDS_MeshVolume*) svol;
11405 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11406 double values[3] = { 0,0,0 };
11407 vtkIdType npts = 0;
11408 vtkIdType const *pts(nullptr);
11409 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11410 for ( vtkIdType i = 0; i < npts; ++i )
11412 double *coords = grid->GetPoint( pts[i] );
11413 for ( int j = 0; j < 3; ++j )
11414 values[j] += coords[j] / npts;
11418 gref.SetCoord( values[0], values[1], values[2] );
11419 angleDom[idom] = 0;
11423 gp_Pnt g( values[0], values[1], values[2] );
11424 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11425 //MESSAGE(" angle=" << angleDom[idom]);
11431 map<double, int> sortedDom; // sort domains by angle
11432 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11433 sortedDom[ia->second] = ia->first;
11434 vector<int> vnodes;
11436 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11438 vdom.push_back(ib->second);
11439 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11441 for (int ino = 0; ino < nbNodes; ino++)
11442 vnodes.push_back(nodes[ino]);
11443 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11452 // --- iterate on shared faces (volumes to modify, face to extrude)
11453 // get node id's of the face (id SMDS = id VTK)
11454 // create flat element with old and new nodes if requested
11456 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11457 // (domain1 X domain2) = domain1 + MAXINT*domain2
11459 std::map<int, std::map<long,int> > nodeQuadDomains;
11460 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11462 //MESSAGE(".. Creation of elements: simple junction");
11463 if (createJointElems)
11465 string joints2DName = "joints2D";
11466 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11467 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11468 string joints3DName = "joints3D";
11469 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11470 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11472 itface = faceDomains.begin();
11473 for (; itface != faceDomains.end(); ++itface)
11475 DownIdType face = itface->first;
11476 std::set<int> oldNodes;
11477 std::set<int>::iterator itn;
11479 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11481 std::map<int, int> domvol = itface->second;
11482 std::map<int, int>::iterator itdom = domvol.begin();
11483 int dom1 = itdom->first;
11484 int vtkVolId = itdom->second;
11486 int dom2 = itdom->first;
11487 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11489 stringstream grpname;
11492 grpname << dom1 << "_" << dom2;
11494 grpname << dom2 << "_" << dom1;
11495 string namegrp = grpname.str();
11496 if (!mapOfJunctionGroups.count(namegrp))
11497 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11498 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11500 sgrp->Add(vol->GetID());
11501 if (vol->GetType() == SMDSAbs_Volume)
11502 joints3DGrp->Add(vol->GetID());
11503 else if (vol->GetType() == SMDSAbs_Face)
11504 joints2DGrp->Add(vol->GetID());
11508 // --- create volumes on multiple domain intersection if requested
11509 // iterate on mutipleNodesToFace
11510 // iterate on edgesMultiDomains
11512 //MESSAGE(".. Creation of elements: multiple junction");
11513 if (createJointElems)
11515 // --- iterate on mutipleNodesToFace
11517 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11518 for (; itn != mutipleNodesToFace.end(); ++itn)
11520 int node = itn->first;
11521 vector<int> orderDom = itn->second;
11522 vector<vtkIdType> orderedNodes;
11523 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11524 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11525 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11527 stringstream grpname;
11529 grpname << 0 << "_" << 0;
11530 string namegrp = grpname.str();
11531 if (!mapOfJunctionGroups.count(namegrp))
11532 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11533 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11535 sgrp->Add(face->GetID());
11538 // --- iterate on edgesMultiDomains
11540 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11541 for (; ite != edgesMultiDomains.end(); ++ite)
11543 vector<int> nodes = ite->first;
11544 vector<int> orderDom = ite->second;
11545 vector<vtkIdType> orderedNodes;
11546 if (nodes.size() == 2)
11548 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11549 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11550 if ( orderDom.size() == 3 )
11551 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11552 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11554 for (int idom = orderDom.size()-1; idom >=0; idom--)
11555 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11556 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11558 string namegrp = "jointsMultiples";
11559 if (!mapOfJunctionGroups.count(namegrp))
11560 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11561 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11563 sgrp->Add(vol->GetID());
11567 //INFOS("Quadratic multiple joints not implemented");
11568 // TODO quadratic nodes
11573 // --- list the explicit faces and edges of the mesh that need to be modified,
11574 // i.e. faces and edges built with one or more duplicated nodes.
11575 // associate these faces or edges to their corresponding domain.
11576 // only the first domain found is kept when a face or edge is shared
11578 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11579 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11580 faceOrEdgeDom.clear();
11583 //MESSAGE(".. Modification of elements");
11584 for (int idomain = idom0; idomain < nbDomains; idomain++)
11586 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11587 for (; itnod != nodeDomains.end(); ++itnod)
11589 int oldId = itnod->first;
11590 //MESSAGE(" node " << oldId);
11591 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11592 for (int i = 0; i < l.ncells; i++)
11594 int vtkId = l.cells[i];
11595 int vtkType = grid->GetCellType(vtkId);
11596 int downId = grid->CellIdToDownId(vtkId);
11598 continue; // new cells: not to be modified
11599 DownIdType aCell(downId, vtkType);
11600 int volParents[1000];
11601 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11602 for (int j = 0; j < nbvol; j++)
11603 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11604 if (!feDom.count(vtkId))
11606 feDom[vtkId] = idomain;
11607 faceOrEdgeDom[aCell] = emptyMap;
11608 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11609 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11610 // << " type " << vtkType << " downId " << downId);
11616 // --- iterate on shared faces (volumes to modify, face to extrude)
11617 // get node id's of the face
11618 // replace old nodes by new nodes in volumes, and update inverse connectivity
11620 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11621 for (int m=0; m<3; m++)
11623 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11624 itface = (*amap).begin();
11625 for (; itface != (*amap).end(); ++itface)
11627 DownIdType face = itface->first;
11628 std::set<int> oldNodes;
11629 std::set<int>::iterator itn;
11631 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11632 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11633 std::map<int, int> localClonedNodeIds;
11635 std::map<int, int> domvol = itface->second;
11636 std::map<int, int>::iterator itdom = domvol.begin();
11637 for (; itdom != domvol.end(); ++itdom)
11639 int idom = itdom->first;
11640 int vtkVolId = itdom->second;
11641 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11642 localClonedNodeIds.clear();
11643 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11646 if (nodeDomains[oldId].count(idom))
11648 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11649 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11652 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11657 // Remove empty groups (issue 0022812)
11658 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11659 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11661 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11662 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11665 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11666 grid->DeleteLinks();
11674 * \brief Double nodes on some external faces and create flat elements.
11675 * Flat elements are mainly used by some types of mechanic calculations.
11677 * Each group of the list must be constituted of faces.
11678 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11679 * @param theElems - list of groups of faces, where a group of faces is a set of
11680 * SMDS_MeshElements sorted by Id.
11681 * @return TRUE if operation has been completed successfully, FALSE otherwise
11683 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11685 // MESSAGE("-------------------------------------------------");
11686 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11687 // MESSAGE("-------------------------------------------------");
11689 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11691 // --- For each group of faces
11692 // duplicate the nodes, create a flat element based on the face
11693 // replace the nodes of the faces by their clones
11695 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11696 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11697 clonedNodes.clear();
11698 intermediateNodes.clear();
11699 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11700 mapOfJunctionGroups.clear();
11702 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11704 const TIDSortedElemSet& domain = theElems[idom];
11705 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11706 for ( ; elemItr != domain.end(); ++elemItr )
11708 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11711 // MESSAGE("aFace=" << aFace->GetID());
11712 bool isQuad = aFace->IsQuadratic();
11713 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11715 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11717 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11718 while (nodeIt->more())
11720 const SMDS_MeshNode* node = nodeIt->next();
11721 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11723 ln2.push_back(node);
11725 ln0.push_back(node);
11727 const SMDS_MeshNode* clone = 0;
11728 if (!clonedNodes.count(node))
11730 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11731 copyPosition( node, clone );
11732 clonedNodes[node] = clone;
11735 clone = clonedNodes[node];
11738 ln3.push_back(clone);
11740 ln1.push_back(clone);
11742 const SMDS_MeshNode* inter = 0;
11743 if (isQuad && (!isMedium))
11745 if (!intermediateNodes.count(node))
11747 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11748 copyPosition( node, inter );
11749 intermediateNodes[node] = inter;
11752 inter = intermediateNodes[node];
11753 ln4.push_back(inter);
11757 // --- extrude the face
11759 vector<const SMDS_MeshNode*> ln;
11760 SMDS_MeshVolume* vol = 0;
11761 vtkIdType aType = aFace->GetVtkType();
11765 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11766 // MESSAGE("vol prism " << vol->GetID());
11767 ln.push_back(ln1[0]);
11768 ln.push_back(ln1[1]);
11769 ln.push_back(ln1[2]);
11772 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11773 // MESSAGE("vol hexa " << vol->GetID());
11774 ln.push_back(ln1[0]);
11775 ln.push_back(ln1[1]);
11776 ln.push_back(ln1[2]);
11777 ln.push_back(ln1[3]);
11779 case VTK_QUADRATIC_TRIANGLE:
11780 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11781 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11782 // MESSAGE("vol quad prism " << vol->GetID());
11783 ln.push_back(ln1[0]);
11784 ln.push_back(ln1[1]);
11785 ln.push_back(ln1[2]);
11786 ln.push_back(ln3[0]);
11787 ln.push_back(ln3[1]);
11788 ln.push_back(ln3[2]);
11790 case VTK_QUADRATIC_QUAD:
11791 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11792 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11793 // ln4[0], ln4[1], ln4[2], ln4[3]);
11794 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11795 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11796 ln4[0], ln4[1], ln4[2], ln4[3]);
11797 // MESSAGE("vol quad hexa " << vol->GetID());
11798 ln.push_back(ln1[0]);
11799 ln.push_back(ln1[1]);
11800 ln.push_back(ln1[2]);
11801 ln.push_back(ln1[3]);
11802 ln.push_back(ln3[0]);
11803 ln.push_back(ln3[1]);
11804 ln.push_back(ln3[2]);
11805 ln.push_back(ln3[3]);
11815 stringstream grpname;
11818 string namegrp = grpname.str();
11819 if (!mapOfJunctionGroups.count(namegrp))
11820 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11821 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11823 sgrp->Add(vol->GetID());
11826 // --- modify the face
11828 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11835 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11836 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11837 * groups of faces to remove inside the object, (idem edges).
11838 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11840 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11841 const TopoDS_Shape& theShape,
11842 SMESH_NodeSearcher* theNodeSearcher,
11843 const char* groupName,
11844 std::vector<double>& nodesCoords,
11845 std::vector<std::vector<int> >& listOfListOfNodes)
11847 // MESSAGE("--------------------------------");
11848 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11849 // MESSAGE("--------------------------------");
11851 // --- zone of volumes to remove is given :
11852 // 1 either by a geom shape (one or more vertices) and a radius,
11853 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11854 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11855 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11856 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11857 // defined by it's name.
11859 SMESHDS_GroupBase* groupDS = 0;
11860 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11861 while ( groupIt->more() )
11864 SMESH_Group * group = groupIt->next();
11865 if ( !group ) continue;
11866 groupDS = group->GetGroupDS();
11867 if ( !groupDS || groupDS->IsEmpty() ) continue;
11868 std::string grpName = group->GetName();
11869 //MESSAGE("grpName=" << grpName);
11870 if (grpName == groupName)
11876 bool isNodeGroup = false;
11877 bool isNodeCoords = false;
11880 if (groupDS->GetType() != SMDSAbs_Node)
11882 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11885 if (nodesCoords.size() > 0)
11886 isNodeCoords = true; // a list o nodes given by their coordinates
11887 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11889 // --- define groups to build
11891 // --- group of SMDS volumes
11892 string grpvName = groupName;
11893 grpvName += "_vol";
11894 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11897 MESSAGE("group not created " << grpvName);
11900 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11902 // --- group of SMDS faces on the skin
11903 string grpsName = groupName;
11904 grpsName += "_skin";
11905 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11908 MESSAGE("group not created " << grpsName);
11911 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11913 // --- group of SMDS faces internal (several shapes)
11914 string grpiName = groupName;
11915 grpiName += "_internalFaces";
11916 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11919 MESSAGE("group not created " << grpiName);
11922 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11924 // --- group of SMDS faces internal (several shapes)
11925 string grpeiName = groupName;
11926 grpeiName += "_internalEdges";
11927 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11930 MESSAGE("group not created " << grpeiName);
11933 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11935 // --- build downward connectivity
11937 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11938 meshDS->BuildDownWardConnectivity(true);
11939 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11941 // --- set of volumes detected inside
11943 std::set<int> setOfInsideVol;
11944 std::set<int> setOfVolToCheck;
11946 std::vector<gp_Pnt> gpnts;
11949 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11951 //MESSAGE("group of nodes provided");
11952 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11953 while ( elemIt->more() )
11955 const SMDS_MeshElement* elem = elemIt->next();
11958 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11961 SMDS_MeshElement* vol = 0;
11962 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11963 while (volItr->more())
11965 vol = (SMDS_MeshElement*)volItr->next();
11966 setOfInsideVol.insert(vol->GetVtkID());
11967 sgrp->Add(vol->GetID());
11971 else if (isNodeCoords)
11973 //MESSAGE("list of nodes coordinates provided");
11976 while ( i < nodesCoords.size()-2 )
11978 double x = nodesCoords[i++];
11979 double y = nodesCoords[i++];
11980 double z = nodesCoords[i++];
11981 gp_Pnt p = gp_Pnt(x, y ,z);
11982 gpnts.push_back(p);
11983 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11987 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11989 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11990 TopTools_IndexedMapOfShape vertexMap;
11991 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11992 gp_Pnt p = gp_Pnt(0,0,0);
11993 if (vertexMap.Extent() < 1)
11996 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11998 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11999 p = BRep_Tool::Pnt(vertex);
12000 gpnts.push_back(p);
12001 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12005 if (gpnts.size() > 0)
12007 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12008 //MESSAGE("startNode->nodeId " << nodeId);
12010 double radius2 = radius*radius;
12011 //MESSAGE("radius2 " << radius2);
12013 // --- volumes on start node
12015 setOfVolToCheck.clear();
12016 SMDS_MeshElement* startVol = 0;
12017 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12018 while (volItr->more())
12020 startVol = (SMDS_MeshElement*)volItr->next();
12021 setOfVolToCheck.insert(startVol->GetVtkID());
12023 if (setOfVolToCheck.empty())
12025 MESSAGE("No volumes found");
12029 // --- starting with central volumes then their neighbors, check if they are inside
12030 // or outside the domain, until no more new neighbor volume is inside.
12031 // Fill the group of inside volumes
12033 std::map<int, double> mapOfNodeDistance2;
12034 mapOfNodeDistance2.clear();
12035 std::set<int> setOfOutsideVol;
12036 while (!setOfVolToCheck.empty())
12038 std::set<int>::iterator it = setOfVolToCheck.begin();
12040 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12041 bool volInside = false;
12042 vtkIdType npts = 0;
12043 vtkIdType const *pts(nullptr);
12044 grid->GetCellPoints(vtkId, npts, pts);
12045 for (int i=0; i<npts; i++)
12047 double distance2 = 0;
12048 if (mapOfNodeDistance2.count(pts[i]))
12050 distance2 = mapOfNodeDistance2[pts[i]];
12051 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12055 double *coords = grid->GetPoint(pts[i]);
12056 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12058 for ( size_t j = 0; j < gpnts.size(); j++ )
12060 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12061 if (d2 < distance2)
12064 if (distance2 < radius2)
12068 mapOfNodeDistance2[pts[i]] = distance2;
12069 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12071 if (distance2 < radius2)
12073 volInside = true; // one or more nodes inside the domain
12074 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12080 setOfInsideVol.insert(vtkId);
12081 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12082 int neighborsVtkIds[NBMAXNEIGHBORS];
12083 int downIds[NBMAXNEIGHBORS];
12084 unsigned char downTypes[NBMAXNEIGHBORS];
12085 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12086 for (int n = 0; n < nbNeighbors; n++)
12087 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12088 setOfVolToCheck.insert(neighborsVtkIds[n]);
12092 setOfOutsideVol.insert(vtkId);
12093 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12095 setOfVolToCheck.erase(vtkId);
12099 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12100 // If yes, add the volume to the inside set
12102 bool addedInside = true;
12103 std::set<int> setOfVolToReCheck;
12104 while (addedInside)
12106 //MESSAGE(" --------------------------- re check");
12107 addedInside = false;
12108 std::set<int>::iterator itv = setOfInsideVol.begin();
12109 for (; itv != setOfInsideVol.end(); ++itv)
12112 int neighborsVtkIds[NBMAXNEIGHBORS];
12113 int downIds[NBMAXNEIGHBORS];
12114 unsigned char downTypes[NBMAXNEIGHBORS];
12115 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12116 for (int n = 0; n < nbNeighbors; n++)
12117 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12118 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12120 setOfVolToCheck = setOfVolToReCheck;
12121 setOfVolToReCheck.clear();
12122 while (!setOfVolToCheck.empty())
12124 std::set<int>::iterator it = setOfVolToCheck.begin();
12126 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12128 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12129 int countInside = 0;
12130 int neighborsVtkIds[NBMAXNEIGHBORS];
12131 int downIds[NBMAXNEIGHBORS];
12132 unsigned char downTypes[NBMAXNEIGHBORS];
12133 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12134 for (int n = 0; n < nbNeighbors; n++)
12135 if (setOfInsideVol.count(neighborsVtkIds[n]))
12137 //MESSAGE("countInside " << countInside);
12138 if (countInside > 1)
12140 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12141 setOfInsideVol.insert(vtkId);
12142 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12143 addedInside = true;
12146 setOfVolToReCheck.insert(vtkId);
12148 setOfVolToCheck.erase(vtkId);
12152 // --- map of Downward faces at the boundary, inside the global volume
12153 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12154 // fill group of SMDS faces inside the volume (when several volume shapes)
12155 // fill group of SMDS faces on the skin of the global volume (if skin)
12157 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12158 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12159 std::set<int>::iterator it = setOfInsideVol.begin();
12160 for (; it != setOfInsideVol.end(); ++it)
12163 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12164 int neighborsVtkIds[NBMAXNEIGHBORS];
12165 int downIds[NBMAXNEIGHBORS];
12166 unsigned char downTypes[NBMAXNEIGHBORS];
12167 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12168 for (int n = 0; n < nbNeighbors; n++)
12170 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12171 if (neighborDim == 3)
12173 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12175 DownIdType face(downIds[n], downTypes[n]);
12176 boundaryFaces[face] = vtkId;
12178 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12179 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12180 if (vtkFaceId >= 0)
12182 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12183 // find also the smds edges on this face
12184 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12185 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12186 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12187 for (int i = 0; i < nbEdges; i++)
12189 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12190 if (vtkEdgeId >= 0)
12191 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12195 else if (neighborDim == 2) // skin of the volume
12197 DownIdType face(downIds[n], downTypes[n]);
12198 skinFaces[face] = vtkId;
12199 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12200 if (vtkFaceId >= 0)
12201 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12206 // --- identify the edges constituting the wire of each subshape on the skin
12207 // define polylines with the nodes of edges, equivalent to wires
12208 // project polylines on subshapes, and partition, to get geom faces
12210 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12211 std::set<int> emptySet;
12213 std::set<int> shapeIds;
12215 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12216 while (itelem->more())
12218 const SMDS_MeshElement *elem = itelem->next();
12219 int shapeId = elem->getshapeId();
12220 int vtkId = elem->GetVtkID();
12221 if (!shapeIdToVtkIdSet.count(shapeId))
12223 shapeIdToVtkIdSet[shapeId] = emptySet;
12224 shapeIds.insert(shapeId);
12226 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12229 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12230 std::set<DownIdType, DownIdCompare> emptyEdges;
12231 emptyEdges.clear();
12233 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12234 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12236 int shapeId = itShape->first;
12237 //MESSAGE(" --- Shape ID --- "<< shapeId);
12238 shapeIdToEdges[shapeId] = emptyEdges;
12240 std::vector<int> nodesEdges;
12242 std::set<int>::iterator its = itShape->second.begin();
12243 for (; its != itShape->second.end(); ++its)
12246 //MESSAGE(" " << vtkId);
12247 int neighborsVtkIds[NBMAXNEIGHBORS];
12248 int downIds[NBMAXNEIGHBORS];
12249 unsigned char downTypes[NBMAXNEIGHBORS];
12250 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12251 for (int n = 0; n < nbNeighbors; n++)
12253 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12255 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12256 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12257 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12259 DownIdType edge(downIds[n], downTypes[n]);
12260 if (!shapeIdToEdges[shapeId].count(edge))
12262 shapeIdToEdges[shapeId].insert(edge);
12264 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12265 nodesEdges.push_back(vtkNodeId[0]);
12266 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12267 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12273 std::list<int> order;
12275 if (nodesEdges.size() > 0)
12277 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12278 nodesEdges[0] = -1;
12279 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12280 nodesEdges[1] = -1; // do not reuse this edge
12284 int nodeTofind = order.back(); // try first to push back
12286 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12287 if (nodesEdges[i] == nodeTofind)
12289 if ( i == (int) nodesEdges.size() )
12290 found = false; // no follower found on back
12293 if (i%2) // odd ==> use the previous one
12294 if (nodesEdges[i-1] < 0)
12298 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12299 nodesEdges[i-1] = -1;
12301 else // even ==> use the next one
12302 if (nodesEdges[i+1] < 0)
12306 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12307 nodesEdges[i+1] = -1;
12312 // try to push front
12314 nodeTofind = order.front(); // try to push front
12315 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12316 if ( nodesEdges[i] == nodeTofind )
12318 if ( i == (int)nodesEdges.size() )
12320 found = false; // no predecessor found on front
12323 if (i%2) // odd ==> use the previous one
12324 if (nodesEdges[i-1] < 0)
12328 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12329 nodesEdges[i-1] = -1;
12331 else // even ==> use the next one
12332 if (nodesEdges[i+1] < 0)
12336 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12337 nodesEdges[i+1] = -1;
12343 std::vector<int> nodes;
12344 nodes.push_back(shapeId);
12345 std::list<int>::iterator itl = order.begin();
12346 for (; itl != order.end(); itl++)
12348 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12349 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12351 listOfListOfNodes.push_back(nodes);
12354 // partition geom faces with blocFissure
12355 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12356 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12362 //================================================================================
12364 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12365 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12366 * \return TRUE if operation has been completed successfully, FALSE otherwise
12368 //================================================================================
12370 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12372 // iterates on volume elements and detect all free faces on them
12373 SMESHDS_Mesh* aMesh = GetMeshDS();
12377 ElemFeatures faceType( SMDSAbs_Face );
12378 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12379 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12382 const SMDS_MeshVolume* volume = vIt->next();
12383 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12384 vTool.SetExternalNormal();
12385 const int iQuad = volume->IsQuadratic();
12386 faceType.SetQuad( iQuad );
12387 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12389 if (!vTool.IsFreeFace(iface))
12392 vector<const SMDS_MeshNode *> nodes;
12393 int nbFaceNodes = vTool.NbFaceNodes(iface);
12394 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12396 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12397 nodes.push_back(faceNodes[inode]);
12399 if (iQuad) // add medium nodes
12401 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12402 nodes.push_back(faceNodes[inode]);
12403 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12404 nodes.push_back(faceNodes[8]);
12406 // add new face based on volume nodes
12407 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12409 nbExisted++; // face already exists
12413 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12418 return ( nbFree == ( nbExisted + nbCreated ));
12423 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12425 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12427 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12430 //================================================================================
12432 * \brief Creates missing boundary elements
12433 * \param elements - elements whose boundary is to be checked
12434 * \param dimension - defines type of boundary elements to create
12435 * \param group - a group to store created boundary elements in
12436 * \param targetMesh - a mesh to store created boundary elements in
12437 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12438 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12439 * boundary elements will be copied into the targetMesh
12440 * \param toAddExistingBondary - if true, not only new but also pre-existing
12441 * boundary elements will be added into the new group
12442 * \param aroundElements - if true, elements will be created on boundary of given
12443 * elements else, on boundary of the whole mesh.
12444 * \return nb of added boundary elements
12446 //================================================================================
12448 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12449 Bnd_Dimension dimension,
12450 SMESH_Group* group/*=0*/,
12451 SMESH_Mesh* targetMesh/*=0*/,
12452 bool toCopyElements/*=false*/,
12453 bool toCopyExistingBoundary/*=false*/,
12454 bool toAddExistingBondary/*= false*/,
12455 bool aroundElements/*= false*/)
12457 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12458 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12459 // hope that all elements are of the same type, do not check them all
12460 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12461 throw SALOME_Exception(LOCALIZED("wrong element type"));
12464 toCopyElements = toCopyExistingBoundary = false;
12466 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12467 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12468 int nbAddedBnd = 0;
12470 // editor adding present bnd elements and optionally holding elements to add to the group
12471 SMESH_MeshEditor* presentEditor;
12472 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12473 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12475 SMESH_MesherHelper helper( *myMesh );
12476 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12477 SMDS_VolumeTool vTool;
12478 TIDSortedElemSet avoidSet;
12479 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12482 typedef vector<const SMDS_MeshNode*> TConnectivity;
12483 TConnectivity tgtNodes;
12484 ElemFeatures elemKind( missType ), elemToCopy;
12486 vector<const SMDS_MeshElement*> presentBndElems;
12487 vector<TConnectivity> missingBndElems;
12488 vector<int> freeFacets;
12489 TConnectivity nodes, elemNodes;
12491 SMDS_ElemIteratorPtr eIt;
12492 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12493 else eIt = SMESHUtils::elemSetIterator( elements );
12495 while ( eIt->more() )
12497 const SMDS_MeshElement* elem = eIt->next();
12498 const int iQuad = elem->IsQuadratic();
12499 elemKind.SetQuad( iQuad );
12501 // ------------------------------------------------------------------------------------
12502 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12503 // ------------------------------------------------------------------------------------
12504 presentBndElems.clear();
12505 missingBndElems.clear();
12506 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12507 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12509 const SMDS_MeshElement* otherVol = 0;
12510 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12512 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12513 ( !aroundElements || elements.count( otherVol )))
12515 freeFacets.push_back( iface );
12517 if ( missType == SMDSAbs_Face )
12518 vTool.SetExternalNormal();
12519 for ( size_t i = 0; i < freeFacets.size(); ++i )
12521 int iface = freeFacets[i];
12522 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12523 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12524 if ( missType == SMDSAbs_Edge ) // boundary edges
12526 nodes.resize( 2+iQuad );
12527 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12529 for ( size_t j = 0; j < nodes.size(); ++j )
12530 nodes[ j ] = nn[ i+j ];
12531 if ( const SMDS_MeshElement* edge =
12532 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12533 presentBndElems.push_back( edge );
12535 missingBndElems.push_back( nodes );
12538 else // boundary face
12541 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12542 nodes.push_back( nn[inode] ); // add corner nodes
12544 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12545 nodes.push_back( nn[inode] ); // add medium nodes
12546 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12548 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12550 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12551 SMDSAbs_Face, /*noMedium=*/false ))
12552 presentBndElems.push_back( f );
12554 missingBndElems.push_back( nodes );
12556 if ( targetMesh != myMesh )
12558 // add 1D elements on face boundary to be added to a new mesh
12559 const SMDS_MeshElement* edge;
12560 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12563 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12565 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12566 if ( edge && avoidSet.insert( edge ).second )
12567 presentBndElems.push_back( edge );
12573 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12575 avoidSet.clear(), avoidSet.insert( elem );
12576 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12577 SMDS_MeshElement::iterator() );
12578 elemNodes.push_back( elemNodes[0] );
12579 nodes.resize( 2 + iQuad );
12580 const int nbLinks = elem->NbCornerNodes();
12581 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12583 nodes[0] = elemNodes[iN];
12584 nodes[1] = elemNodes[iN+1+iQuad];
12585 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12586 continue; // not free link
12588 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12589 if ( const SMDS_MeshElement* edge =
12590 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12591 presentBndElems.push_back( edge );
12593 missingBndElems.push_back( nodes );
12597 // ---------------------------------
12598 // 2. Add missing boundary elements
12599 // ---------------------------------
12600 if ( targetMesh != myMesh )
12601 // instead of making a map of nodes in this mesh and targetMesh,
12602 // we create nodes with same IDs.
12603 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12605 TConnectivity& srcNodes = missingBndElems[i];
12606 tgtNodes.resize( srcNodes.size() );
12607 for ( inode = 0; inode < srcNodes.size(); ++inode )
12608 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12609 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12611 /*noMedium=*/false))
12613 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12617 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12619 TConnectivity& nodes = missingBndElems[ i ];
12620 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12622 /*noMedium=*/false))
12624 SMDS_MeshElement* newElem =
12625 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12626 nbAddedBnd += bool( newElem );
12628 // try to set a new element to a shape
12629 if ( myMesh->HasShapeToMesh() )
12632 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12633 const size_t nbN = nodes.size() / (iQuad+1 );
12634 for ( inode = 0; inode < nbN && ok; ++inode )
12636 pair<int, TopAbs_ShapeEnum> i_stype =
12637 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12638 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12639 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12641 if ( ok && mediumShapes.size() > 1 )
12643 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12644 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12645 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12647 if (( ok = ( stype_i->first != stype_i_0.first )))
12648 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12649 aMesh->IndexToShape( stype_i_0.second ));
12652 if ( ok && mediumShapes.begin()->first == missShapeType )
12653 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12657 // ----------------------------------
12658 // 3. Copy present boundary elements
12659 // ----------------------------------
12660 if ( toCopyExistingBoundary )
12661 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12663 const SMDS_MeshElement* e = presentBndElems[i];
12664 tgtNodes.resize( e->NbNodes() );
12665 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12666 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12667 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12669 else // store present elements to add them to a group
12670 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12672 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12675 } // loop on given elements
12677 // ---------------------------------------------
12678 // 4. Fill group with boundary elements
12679 // ---------------------------------------------
12682 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12683 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12684 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12686 tgtEditor.myLastCreatedElems.clear();
12687 tgtEditor2.myLastCreatedElems.clear();
12689 // -----------------------
12690 // 5. Copy given elements
12691 // -----------------------
12692 if ( toCopyElements && targetMesh != myMesh )
12694 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12695 else eIt = SMESHUtils::elemSetIterator( elements );
12696 while (eIt->more())
12698 const SMDS_MeshElement* elem = eIt->next();
12699 tgtNodes.resize( elem->NbNodes() );
12700 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12701 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12702 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12704 tgtEditor.myLastCreatedElems.clear();
12710 //================================================================================
12712 * \brief Copy node position and set \a to node on the same geometry
12714 //================================================================================
12716 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12717 const SMDS_MeshNode* to )
12719 if ( !from || !to ) return;
12721 SMDS_PositionPtr pos = from->GetPosition();
12722 if ( !pos || from->getshapeId() < 1 ) return;
12724 switch ( pos->GetTypeOfPosition() )
12726 case SMDS_TOP_3DSPACE: break;
12728 case SMDS_TOP_FACE:
12730 SMDS_FacePositionPtr fPos = pos;
12731 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12732 fPos->GetUParameter(), fPos->GetVParameter() );
12735 case SMDS_TOP_EDGE:
12737 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12738 SMDS_EdgePositionPtr ePos = pos;
12739 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12742 case SMDS_TOP_VERTEX:
12744 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12747 case SMDS_TOP_UNSPEC: