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 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2074 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2076 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2078 static TSplitMethod to4methods[4]; // order BT, LR, FB
2079 if ( to4methods[iF]._nbSplits == 0 )
2083 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2084 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2085 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2088 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2089 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2090 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2093 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2094 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2095 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2097 default: return to4methods[3];
2099 to4methods[iF]._nbSplits = 4;
2100 to4methods[iF]._nbCorners = 6;
2102 return to4methods[iF];
2104 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2106 TSplitMethod method;
2108 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2110 const int nbVariants = 2, nbSplits = 2;
2111 const int** connVariants = 0;
2113 case 0: connVariants = theHexTo2Prisms_BT; break;
2114 case 1: connVariants = theHexTo2Prisms_LR; break;
2115 case 2: connVariants = theHexTo2Prisms_FB; break;
2116 default: return method;
2119 // look for prisms adjacent via facetToSplit and an opposite one
2120 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2122 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2123 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2124 if ( nbNodes != 4 ) return method;
2126 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2127 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2132 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2137 // there are adjacent prism
2138 for ( int variant = 0; variant < nbVariants; ++variant )
2140 // check method compliance with adjacent prisms,
2141 // the found prism facets must be among facets of prisms described by current method
2142 method._nbSplits = nbSplits;
2143 method._nbCorners = 6;
2144 method._connectivity = connVariants[ variant ];
2145 if ( method.hasFacet( *t ))
2150 // No adjacent prisms. Select a variant with a best aspect ratio.
2152 double badness[2] = { 0., 0. };
2153 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2154 const SMDS_MeshNode** nodes = vol.GetNodes();
2155 for ( int variant = 0; variant < nbVariants; ++variant )
2156 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2158 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2159 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2161 method._connectivity = connVariants[ variant ];
2162 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2163 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2164 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2166 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2169 badness[ variant ] += getBadRate( &tria, aspectRatio );
2171 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2173 method._nbSplits = nbSplits;
2174 method._nbCorners = 6;
2175 method._connectivity = connVariants[ iBetter ];
2180 //================================================================================
2182 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2184 //================================================================================
2186 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2187 const SMDSAbs_GeometryType geom ) const
2189 // find the tetrahedron including the three nodes of facet
2190 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2191 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2192 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2193 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2194 while ( volIt1->more() )
2196 const SMDS_MeshElement* v = volIt1->next();
2197 if ( v->GetGeomType() != geom )
2199 const int lastCornerInd = v->NbCornerNodes() - 1;
2200 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2201 continue; // medium node not allowed
2202 const int ind2 = v->GetNodeIndex( n2 );
2203 if ( ind2 < 0 || lastCornerInd < ind2 )
2205 const int ind3 = v->GetNodeIndex( n3 );
2206 if ( ind3 < 0 || lastCornerInd < ind3 )
2213 //=======================================================================
2215 * \brief A key of a face of volume
2217 //=======================================================================
2219 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2221 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2223 TIDSortedNodeSet sortedNodes;
2224 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2225 int nbNodes = vol.NbFaceNodes( iF );
2226 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2227 for ( int i = 0; i < nbNodes; i += iQ )
2228 sortedNodes.insert( fNodes[i] );
2229 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2230 first.first = (*(n++))->GetID();
2231 first.second = (*(n++))->GetID();
2232 second.first = (*(n++))->GetID();
2233 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2238 //=======================================================================
2239 //function : SplitVolumes
2240 //purpose : Split volume elements into tetrahedra or prisms.
2241 // If facet ID < 0, element is split into tetrahedra,
2242 // else a hexahedron is split into prisms so that the given facet is
2243 // split into triangles
2244 //=======================================================================
2246 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2247 const int theMethodFlags)
2249 SMDS_VolumeTool volTool;
2250 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2251 fHelper.ToFixNodeParameters( true );
2253 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2254 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2256 SMESH_SequenceOfElemPtr newNodes, newElems;
2258 // map face of volume to it's baricenrtic node
2259 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2261 vector<const SMDS_MeshElement* > splitVols;
2263 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2264 for ( ; elem2facet != theElems.end(); ++elem2facet )
2266 const SMDS_MeshElement* elem = elem2facet->first;
2267 const int facetToSplit = elem2facet->second;
2268 if ( elem->GetType() != SMDSAbs_Volume )
2270 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2271 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2274 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2276 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2277 getTetraSplitMethod( volTool, theMethodFlags ) :
2278 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2279 if ( splitMethod._nbSplits < 1 ) continue;
2281 // find submesh to add new tetras to
2282 if ( !subMesh || !subMesh->Contains( elem ))
2284 int shapeID = FindShape( elem );
2285 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2286 subMesh = GetMeshDS()->MeshElements( shapeID );
2289 if ( elem->IsQuadratic() )
2292 // add quadratic links to the helper
2293 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2295 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2296 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2297 for ( int iN = 0; iN < nbN; iN += iQ )
2298 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2300 helper.SetIsQuadratic( true );
2305 helper.SetIsQuadratic( false );
2307 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2308 volTool.GetNodes() + elem->NbNodes() );
2309 helper.SetElementsOnShape( true );
2310 if ( splitMethod._baryNode )
2312 // make a node at barycenter
2313 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2314 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2315 nodes.push_back( gcNode );
2316 newNodes.push_back( gcNode );
2318 if ( !splitMethod._faceBaryNode.empty() )
2320 // make or find baricentric nodes of faces
2321 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2322 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2324 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2325 volFace2BaryNode.insert
2326 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2329 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2330 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2332 nodes.push_back( iF_n->second = f_n->second );
2337 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2338 const int* volConn = splitMethod._connectivity;
2339 if ( splitMethod._nbCorners == 4 ) // tetra
2340 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2341 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2342 nodes[ volConn[1] ],
2343 nodes[ volConn[2] ],
2344 nodes[ volConn[3] ]));
2346 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2347 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2348 nodes[ volConn[1] ],
2349 nodes[ volConn[2] ],
2350 nodes[ volConn[3] ],
2351 nodes[ volConn[4] ],
2352 nodes[ volConn[5] ]));
2354 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2356 // Split faces on sides of the split volume
2358 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2359 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2361 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2362 if ( nbNodes < 4 ) continue;
2364 // find an existing face
2365 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2366 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2367 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2368 /*noMedium=*/false))
2371 helper.SetElementsOnShape( false );
2372 vector< const SMDS_MeshElement* > triangles;
2374 // find submesh to add new triangles in
2375 if ( !fSubMesh || !fSubMesh->Contains( face ))
2377 int shapeID = FindShape( face );
2378 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2380 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2381 if ( iF_n != splitMethod._faceBaryNode.end() )
2383 const SMDS_MeshNode *baryNode = iF_n->second;
2384 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2386 const SMDS_MeshNode* n1 = fNodes[iN];
2387 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2388 const SMDS_MeshNode *n3 = baryNode;
2389 if ( !volTool.IsFaceExternal( iF ))
2391 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2393 if ( fSubMesh ) // update position of the bary node on geometry
2396 subMesh->RemoveNode( baryNode );
2397 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2398 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2399 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2401 fHelper.SetSubShape( s );
2402 gp_XY uv( 1e100, 1e100 );
2404 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2405 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2408 // node is too far from the surface
2409 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2410 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2411 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2418 // among possible triangles create ones described by split method
2419 const int* nInd = volTool.GetFaceNodesIndices( iF );
2420 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2421 int iCom = 0; // common node of triangle faces to split into
2422 list< TTriangleFacet > facets;
2423 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2425 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2426 nInd[ iQ * ( (iCom+1)%nbNodes )],
2427 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2428 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2429 nInd[ iQ * ( (iCom+2)%nbNodes )],
2430 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2431 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2433 facets.push_back( t012 );
2434 facets.push_back( t023 );
2435 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2436 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2437 nInd[ iQ * ((iLast-1)%nbNodes )],
2438 nInd[ iQ * ((iLast )%nbNodes )]));
2442 list< TTriangleFacet >::iterator facet = facets.begin();
2443 if ( facet == facets.end() )
2445 for ( ; facet != facets.end(); ++facet )
2447 if ( !volTool.IsFaceExternal( iF ))
2448 swap( facet->_n2, facet->_n3 );
2449 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2450 volNodes[ facet->_n2 ],
2451 volNodes[ facet->_n3 ]));
2454 for ( size_t i = 0; i < triangles.size(); ++i )
2456 if ( !triangles[ i ]) continue;
2458 fSubMesh->AddElement( triangles[ i ]);
2459 newElems.push_back( triangles[ i ]);
2461 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2462 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2464 } // while a face based on facet nodes exists
2465 } // loop on volume faces to split them into triangles
2467 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2469 if ( geomType == SMDSEntity_TriQuad_Hexa )
2471 // remove medium nodes that could become free
2472 for ( int i = 20; i < volTool.NbNodes(); ++i )
2473 if ( volNodes[i]->NbInverseElements() == 0 )
2474 GetMeshDS()->RemoveNode( volNodes[i] );
2476 } // loop on volumes to split
2478 myLastCreatedNodes = newNodes;
2479 myLastCreatedElems = newElems;
2482 //=======================================================================
2483 //function : GetHexaFacetsToSplit
2484 //purpose : For hexahedra that will be split into prisms, finds facets to
2485 // split into triangles. Only hexahedra adjacent to the one closest
2486 // to theFacetNormal.Location() are returned.
2487 //param [in,out] theHexas - the hexahedra
2488 //param [in] theFacetNormal - facet normal
2489 //param [out] theFacets - the hexahedra and found facet IDs
2490 //=======================================================================
2492 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2493 const gp_Ax1& theFacetNormal,
2494 TFacetOfElem & theFacets)
2496 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2498 // Find a hexa closest to the location of theFacetNormal
2500 const SMDS_MeshElement* startHex;
2502 // get SMDS_ElemIteratorPtr on theHexas
2503 typedef const SMDS_MeshElement* TValue;
2504 typedef TIDSortedElemSet::iterator TSetIterator;
2505 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2506 typedef SMDS_MeshElement::GeomFilter TFilter;
2507 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2508 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2509 ( new TElemSetIter( theHexas.begin(),
2511 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2513 SMESH_ElementSearcher* searcher =
2514 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2516 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2521 throw SALOME_Exception( THIS_METHOD "startHex not found");
2524 // Select a facet of startHex by theFacetNormal
2526 SMDS_VolumeTool vTool( startHex );
2527 double norm[3], dot, maxDot = 0;
2529 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2530 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2532 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2540 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2542 // Fill theFacets starting from facetID of startHex
2544 // facets used for searching of volumes adjacent to already treated ones
2545 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2546 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2547 TFacetMap facetsToCheck;
2549 set<const SMDS_MeshNode*> facetNodes;
2550 const SMDS_MeshElement* curHex;
2552 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2556 // move in two directions from startHex via facetID
2557 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2560 int curFacet = facetID;
2561 if ( is2nd ) // do not treat startHex twice
2563 vTool.Set( curHex );
2564 if ( vTool.IsFreeFace( curFacet, &curHex ))
2570 vTool.GetFaceNodes( curFacet, facetNodes );
2571 vTool.Set( curHex );
2572 curFacet = vTool.GetFaceIndex( facetNodes );
2577 // store a facet to split
2578 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2580 theFacets.insert( make_pair( curHex, -1 ));
2583 if ( !allHex && !theHexas.count( curHex ))
2586 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2587 theFacets.insert( make_pair( curHex, curFacet ));
2588 if ( !facetIt2isNew.second )
2591 // remember not-to-split facets in facetsToCheck
2592 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2593 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2595 if ( iF == curFacet && iF == oppFacet )
2597 TVolumeFaceKey facetKey ( vTool, iF );
2598 TElemFacets elemFacet( facetIt2isNew.first, iF );
2599 pair< TFacetMap::iterator, bool > it2isnew =
2600 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2601 if ( !it2isnew.second )
2602 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2604 // pass to a volume adjacent via oppFacet
2605 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2611 // get a new curFacet
2612 vTool.GetFaceNodes( oppFacet, facetNodes );
2613 vTool.Set( curHex );
2614 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2617 } // move in two directions from startHex via facetID
2619 // Find a new startHex by facetsToCheck
2623 TFacetMap::iterator fIt = facetsToCheck.begin();
2624 while ( !startHex && fIt != facetsToCheck.end() )
2626 const TElemFacets& elemFacets = fIt->second;
2627 const SMDS_MeshElement* hex = elemFacets.first->first;
2628 int splitFacet = elemFacets.first->second;
2629 int lateralFacet = elemFacets.second;
2630 facetsToCheck.erase( fIt );
2631 fIt = facetsToCheck.begin();
2634 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2635 curHex->GetGeomType() != SMDSGeom_HEXA )
2637 if ( !allHex && !theHexas.count( curHex ))
2642 // find a facet of startHex to split
2644 set<const SMDS_MeshNode*> lateralNodes;
2645 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2646 vTool.GetFaceNodes( splitFacet, facetNodes );
2647 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2648 vTool.Set( startHex );
2649 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2651 // look for a facet of startHex having common nodes with facetNodes
2652 // but not lateralFacet
2653 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2655 if ( iF == lateralFacet )
2657 int nbCommonNodes = 0;
2658 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2659 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2660 nbCommonNodes += facetNodes.count( nn[ iN ]);
2662 if ( nbCommonNodes >= 2 )
2669 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2671 } // while ( startHex )
2678 //================================================================================
2680 * \brief Selects nodes of several elements according to a given interlace
2681 * \param [in] srcNodes - nodes to select from
2682 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2683 * \param [in] interlace - indices of nodes for all elements
2684 * \param [in] nbElems - nb of elements
2685 * \param [in] nbNodes - nb of nodes in each element
2686 * \param [in] mesh - the mesh
2687 * \param [out] elemQueue - a list to push elements found by the selected nodes
2688 * \param [in] type - type of elements to look for
2690 //================================================================================
2692 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2693 vector< const SMDS_MeshNode* >* tgtNodesVec,
2694 const int* interlace,
2697 SMESHDS_Mesh* mesh = 0,
2698 list< const SMDS_MeshElement* >* elemQueue=0,
2699 SMDSAbs_ElementType type=SMDSAbs_All)
2701 for ( int iE = 0; iE < nbElems; ++iE )
2703 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2704 const int* select = & interlace[iE*nbNodes];
2705 elemNodes.resize( nbNodes );
2706 for ( int iN = 0; iN < nbNodes; ++iN )
2707 elemNodes[iN] = srcNodes[ select[ iN ]];
2709 const SMDS_MeshElement* e;
2711 for ( int iE = 0; iE < nbElems; ++iE )
2712 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2713 elemQueue->push_back( e );
2717 //=======================================================================
2719 * Split bi-quadratic elements into linear ones without creation of additional nodes
2720 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2721 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2722 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2723 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2724 * will be split in order to keep the mesh conformal.
2725 * \param elems - elements to split
2727 //=======================================================================
2729 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2731 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2732 vector<const SMDS_MeshElement* > splitElems;
2733 list< const SMDS_MeshElement* > elemQueue;
2734 list< const SMDS_MeshElement* >::iterator elemIt;
2736 SMESHDS_Mesh * mesh = GetMeshDS();
2737 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2738 int nbElems, nbNodes;
2740 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2741 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2744 elemQueue.push_back( *elemSetIt );
2745 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2747 const SMDS_MeshElement* elem = *elemIt;
2748 switch( elem->GetEntityType() )
2750 case SMDSEntity_TriQuad_Hexa: // HEX27
2752 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2753 nbElems = nbNodes = 8;
2754 elemType = & hexaType;
2756 // get nodes for new elements
2757 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2758 { 1,9,20,8, 17,22,26,21 },
2759 { 2,10,20,9, 18,23,26,22 },
2760 { 3,11,20,10, 19,24,26,23 },
2761 { 16,21,26,24, 4,12,25,15 },
2762 { 17,22,26,21, 5,13,25,12 },
2763 { 18,23,26,22, 6,14,25,13 },
2764 { 19,24,26,23, 7,15,25,14 }};
2765 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2767 // add boundary faces to elemQueue
2768 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2769 { 4,5,6,7, 12,13,14,15, 25 },
2770 { 0,1,5,4, 8,17,12,16, 21 },
2771 { 1,2,6,5, 9,18,13,17, 22 },
2772 { 2,3,7,6, 10,19,14,18, 23 },
2773 { 3,0,4,7, 11,16,15,19, 24 }};
2774 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2776 // add boundary segments to elemQueue
2777 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2778 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2779 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2780 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2783 case SMDSEntity_BiQuad_Triangle: // TRIA7
2785 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788 elemType = & quadType;
2790 // get nodes for new elements
2791 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2792 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2794 // add boundary segments to elemQueue
2795 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2796 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2799 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2801 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2804 elemType = & quadType;
2806 // get nodes for new elements
2807 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2808 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2810 // add boundary segments to elemQueue
2811 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2812 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2815 case SMDSEntity_Quad_Edge:
2817 if ( elemIt == elemQueue.begin() )
2818 continue; // an elem is in theElems
2819 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2822 elemType = & segType;
2824 // get nodes for new elements
2825 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2826 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2830 } // switch( elem->GetEntityType() )
2832 // Create new elements
2834 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2838 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2839 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2840 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2841 //elemType->SetID( -1 );
2843 for ( int iE = 0; iE < nbElems; ++iE )
2844 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2847 ReplaceElemInGroups( elem, splitElems, mesh );
2850 for ( size_t i = 0; i < splitElems.size(); ++i )
2851 subMesh->AddElement( splitElems[i] );
2856 //=======================================================================
2857 //function : AddToSameGroups
2858 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2859 //=======================================================================
2861 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2862 const SMDS_MeshElement* elemInGroups,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty()) {
2867 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2868 for ( ; grIt != groups.end(); grIt++ ) {
2869 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2870 if ( group && group->Contains( elemInGroups ))
2871 group->SMDSGroup().Add( elemToAdd );
2877 //=======================================================================
2878 //function : RemoveElemFromGroups
2879 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2880 //=======================================================================
2881 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2882 SMESHDS_Mesh * aMesh)
2884 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2885 if (!groups.empty())
2887 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2888 for (; GrIt != groups.end(); GrIt++)
2890 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2891 if (!grp || grp->IsEmpty()) continue;
2892 grp->SMDSGroup().Remove(removeelem);
2897 //================================================================================
2899 * \brief Replace elemToRm by elemToAdd in the all groups
2901 //================================================================================
2903 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2904 const SMDS_MeshElement* elemToAdd,
2905 SMESHDS_Mesh * aMesh)
2907 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2908 if (!groups.empty()) {
2909 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2910 for ( ; grIt != groups.end(); grIt++ ) {
2911 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2912 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2913 group->SMDSGroup().Add( elemToAdd );
2918 //================================================================================
2920 * \brief Replace elemToRm by elemToAdd in the all groups
2922 //================================================================================
2924 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2925 const vector<const SMDS_MeshElement*>& elemToAdd,
2926 SMESHDS_Mesh * aMesh)
2928 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2929 if (!groups.empty())
2931 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2932 for ( ; grIt != groups.end(); grIt++ ) {
2933 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2934 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2935 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2936 group->SMDSGroup().Add( elemToAdd[ i ] );
2941 //=======================================================================
2942 //function : QuadToTri
2943 //purpose : Cut quadrangles into triangles.
2944 // theCrit is used to select a diagonal to cut
2945 //=======================================================================
2947 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2948 const bool the13Diag)
2951 myLastCreatedElems.reserve( theElems.size() * 2 );
2953 SMESHDS_Mesh * aMesh = GetMeshDS();
2954 Handle(Geom_Surface) surface;
2955 SMESH_MesherHelper helper( *GetMesh() );
2957 TIDSortedElemSet::iterator itElem;
2958 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2960 const SMDS_MeshElement* elem = *itElem;
2961 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2964 if ( elem->NbNodes() == 4 ) {
2965 // retrieve element nodes
2966 const SMDS_MeshNode* aNodes [4];
2967 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2969 while ( itN->more() )
2970 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2972 int aShapeId = FindShape( elem );
2973 const SMDS_MeshElement* newElem1 = 0;
2974 const SMDS_MeshElement* newElem2 = 0;
2976 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2977 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2980 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2981 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2983 myLastCreatedElems.push_back(newElem1);
2984 myLastCreatedElems.push_back(newElem2);
2985 // put a new triangle on the same shape and add to the same groups
2988 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2989 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2991 AddToSameGroups( newElem1, elem, aMesh );
2992 AddToSameGroups( newElem2, elem, aMesh );
2993 aMesh->RemoveElement( elem );
2996 // Quadratic quadrangle
2998 else if ( elem->NbNodes() >= 8 )
3000 // get surface elem is on
3001 int aShapeId = FindShape( elem );
3002 if ( aShapeId != helper.GetSubShapeID() ) {
3006 shape = aMesh->IndexToShape( aShapeId );
3007 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3008 TopoDS_Face face = TopoDS::Face( shape );
3009 surface = BRep_Tool::Surface( face );
3010 if ( !surface.IsNull() )
3011 helper.SetSubShape( shape );
3015 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3016 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3017 for ( int i = 0; itN->more(); ++i )
3018 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3020 const SMDS_MeshNode* centrNode = aNodes[8];
3021 if ( centrNode == 0 )
3023 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3024 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3026 myLastCreatedNodes.push_back(centrNode);
3029 // create a new element
3030 const SMDS_MeshElement* newElem1 = 0;
3031 const SMDS_MeshElement* newElem2 = 0;
3033 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3034 aNodes[6], aNodes[7], centrNode );
3035 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3036 centrNode, aNodes[4], aNodes[5] );
3039 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3040 aNodes[7], aNodes[4], centrNode );
3041 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3042 centrNode, aNodes[5], aNodes[6] );
3044 myLastCreatedElems.push_back(newElem1);
3045 myLastCreatedElems.push_back(newElem2);
3046 // put a new triangle on the same shape and add to the same groups
3049 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3050 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3052 AddToSameGroups( newElem1, elem, aMesh );
3053 AddToSameGroups( newElem2, elem, aMesh );
3054 aMesh->RemoveElement( elem );
3061 //=======================================================================
3062 //function : getAngle
3064 //=======================================================================
3066 double getAngle(const SMDS_MeshElement * tr1,
3067 const SMDS_MeshElement * tr2,
3068 const SMDS_MeshNode * n1,
3069 const SMDS_MeshNode * n2)
3071 double angle = 2. * M_PI; // bad angle
3074 SMESH::Controls::TSequenceOfXYZ P1, P2;
3075 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3076 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3079 if(!tr1->IsQuadratic())
3080 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3082 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3083 if ( N1.SquareMagnitude() <= gp::Resolution() )
3085 if(!tr2->IsQuadratic())
3086 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3088 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3089 if ( N2.SquareMagnitude() <= gp::Resolution() )
3092 // find the first diagonal node n1 in the triangles:
3093 // take in account a diagonal link orientation
3094 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3095 for ( int t = 0; t < 2; t++ ) {
3096 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3097 int i = 0, iDiag = -1;
3098 while ( it->more()) {
3099 const SMDS_MeshElement *n = it->next();
3100 if ( n == n1 || n == n2 ) {
3104 if ( i - iDiag == 1 )
3105 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3114 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3117 angle = N1.Angle( N2 );
3122 // =================================================
3123 // class generating a unique ID for a pair of nodes
3124 // and able to return nodes by that ID
3125 // =================================================
3129 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3130 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3133 long GetLinkID (const SMDS_MeshNode * n1,
3134 const SMDS_MeshNode * n2) const
3136 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3139 bool GetNodes (const long theLinkID,
3140 const SMDS_MeshNode* & theNode1,
3141 const SMDS_MeshNode* & theNode2) const
3143 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3144 if ( !theNode1 ) return false;
3145 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3146 if ( !theNode2 ) return false;
3152 const SMESHDS_Mesh* myMesh;
3157 //=======================================================================
3158 //function : TriToQuad
3159 //purpose : Fuse neighbour triangles into quadrangles.
3160 // theCrit is used to select a neighbour to fuse with.
3161 // theMaxAngle is a max angle between element normals at which
3162 // fusion is still performed.
3163 //=======================================================================
3165 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3166 SMESH::Controls::NumericalFunctorPtr theCrit,
3167 const double theMaxAngle)
3170 myLastCreatedElems.reserve( theElems.size() / 2 );
3172 if ( !theCrit.get() )
3175 SMESHDS_Mesh * aMesh = GetMeshDS();
3177 // Prepare data for algo: build
3178 // 1. map of elements with their linkIDs
3179 // 2. map of linkIDs with their elements
3181 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3182 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3183 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3184 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3186 TIDSortedElemSet::iterator itElem;
3187 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3189 const SMDS_MeshElement* elem = *itElem;
3190 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3191 bool IsTria = ( elem->NbCornerNodes()==3 );
3192 if (!IsTria) continue;
3194 // retrieve element nodes
3195 const SMDS_MeshNode* aNodes [4];
3196 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3199 aNodes[ i++ ] = itN->next();
3200 aNodes[ 3 ] = aNodes[ 0 ];
3203 for ( i = 0; i < 3; i++ ) {
3204 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3205 // check if elements sharing a link can be fused
3206 itLE = mapLi_listEl.find( link );
3207 if ( itLE != mapLi_listEl.end() ) {
3208 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3210 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3211 //if ( FindShape( elem ) != FindShape( elem2 ))
3212 // continue; // do not fuse triangles laying on different shapes
3213 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3214 continue; // avoid making badly shaped quads
3215 (*itLE).second.push_back( elem );
3218 mapLi_listEl[ link ].push_back( elem );
3220 mapEl_setLi [ elem ].insert( link );
3223 // Clean the maps from the links shared by a sole element, ie
3224 // links to which only one element is bound in mapLi_listEl
3226 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3227 int nbElems = (*itLE).second.size();
3228 if ( nbElems < 2 ) {
3229 const SMDS_MeshElement* elem = (*itLE).second.front();
3230 SMESH_TLink link = (*itLE).first;
3231 mapEl_setLi[ elem ].erase( link );
3232 if ( mapEl_setLi[ elem ].empty() )
3233 mapEl_setLi.erase( elem );
3237 // Algo: fuse triangles into quadrangles
3239 while ( ! mapEl_setLi.empty() ) {
3240 // Look for the start element:
3241 // the element having the least nb of shared links
3242 const SMDS_MeshElement* startElem = 0;
3244 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3245 int nbLinks = (*itEL).second.size();
3246 if ( nbLinks < minNbLinks ) {
3247 startElem = (*itEL).first;
3248 minNbLinks = nbLinks;
3249 if ( minNbLinks == 1 )
3254 // search elements to fuse starting from startElem or links of elements
3255 // fused earlyer - startLinks
3256 list< SMESH_TLink > startLinks;
3257 while ( startElem || !startLinks.empty() ) {
3258 while ( !startElem && !startLinks.empty() ) {
3259 // Get an element to start, by a link
3260 SMESH_TLink linkId = startLinks.front();
3261 startLinks.pop_front();
3262 itLE = mapLi_listEl.find( linkId );
3263 if ( itLE != mapLi_listEl.end() ) {
3264 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3265 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3266 for ( ; itE != listElem.end() ; itE++ )
3267 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3269 mapLi_listEl.erase( itLE );
3274 // Get candidates to be fused
3275 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3276 const SMESH_TLink *link12 = 0, *link13 = 0;
3278 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3279 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3280 ASSERT( !setLi.empty() );
3281 set< SMESH_TLink >::iterator itLi;
3282 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3284 const SMESH_TLink & link = (*itLi);
3285 itLE = mapLi_listEl.find( link );
3286 if ( itLE == mapLi_listEl.end() )
3289 const SMDS_MeshElement* elem = (*itLE).second.front();
3291 elem = (*itLE).second.back();
3292 mapLi_listEl.erase( itLE );
3293 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3304 // add other links of elem to list of links to re-start from
3305 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3306 set< SMESH_TLink >::iterator it;
3307 for ( it = links.begin(); it != links.end(); it++ ) {
3308 const SMESH_TLink& link2 = (*it);
3309 if ( link2 != link )
3310 startLinks.push_back( link2 );
3314 // Get nodes of possible quadrangles
3315 const SMDS_MeshNode *n12 [4], *n13 [4];
3316 bool Ok12 = false, Ok13 = false;
3317 const SMDS_MeshNode *linkNode1, *linkNode2;
3319 linkNode1 = link12->first;
3320 linkNode2 = link12->second;
3321 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3325 linkNode1 = link13->first;
3326 linkNode2 = link13->second;
3327 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3331 // Choose a pair to fuse
3332 if ( Ok12 && Ok13 ) {
3333 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3334 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3335 double aBadRate12 = getBadRate( &quad12, theCrit );
3336 double aBadRate13 = getBadRate( &quad13, theCrit );
3337 if ( aBadRate13 < aBadRate12 )
3344 // and remove fused elems and remove links from the maps
3345 mapEl_setLi.erase( tr1 );
3348 mapEl_setLi.erase( tr2 );
3349 mapLi_listEl.erase( *link12 );
3350 if ( tr1->NbNodes() == 3 )
3352 const SMDS_MeshElement* newElem = 0;
3353 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3354 myLastCreatedElems.push_back(newElem);
3355 AddToSameGroups( newElem, tr1, aMesh );
3356 int aShapeId = tr1->getshapeId();
3358 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359 aMesh->RemoveElement( tr1 );
3360 aMesh->RemoveElement( tr2 );
3363 vector< const SMDS_MeshNode* > N1;
3364 vector< const SMDS_MeshNode* > N2;
3365 getNodesFromTwoTria(tr1,tr2,N1,N2);
3366 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3367 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3368 // i.e. first nodes from both arrays form a new diagonal
3369 const SMDS_MeshNode* aNodes[8];
3378 const SMDS_MeshElement* newElem = 0;
3379 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3383 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385 myLastCreatedElems.push_back(newElem);
3386 AddToSameGroups( newElem, tr1, aMesh );
3387 int aShapeId = tr1->getshapeId();
3389 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390 aMesh->RemoveElement( tr1 );
3391 aMesh->RemoveElement( tr2 );
3392 // remove middle node (9)
3393 if ( N1[4]->NbInverseElements() == 0 )
3394 aMesh->RemoveNode( N1[4] );
3395 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396 aMesh->RemoveNode( N1[6] );
3397 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N2[6] );
3403 mapEl_setLi.erase( tr3 );
3404 mapLi_listEl.erase( *link13 );
3405 if ( tr1->NbNodes() == 3 ) {
3406 const SMDS_MeshElement* newElem = 0;
3407 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3408 myLastCreatedElems.push_back(newElem);
3409 AddToSameGroups( newElem, tr1, aMesh );
3410 int aShapeId = tr1->getshapeId();
3412 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3413 aMesh->RemoveElement( tr1 );
3414 aMesh->RemoveElement( tr3 );
3417 vector< const SMDS_MeshNode* > N1;
3418 vector< const SMDS_MeshNode* > N2;
3419 getNodesFromTwoTria(tr1,tr3,N1,N2);
3420 // now we receive following N1 and N2 (using numeration as above image)
3421 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3422 // i.e. first nodes from both arrays form a new diagonal
3423 const SMDS_MeshNode* aNodes[8];
3432 const SMDS_MeshElement* newElem = 0;
3433 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3434 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3435 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3437 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3438 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3439 myLastCreatedElems.push_back(newElem);
3440 AddToSameGroups( newElem, tr1, aMesh );
3441 int aShapeId = tr1->getshapeId();
3443 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3444 aMesh->RemoveElement( tr1 );
3445 aMesh->RemoveElement( tr3 );
3446 // remove middle node (9)
3447 if ( N1[4]->NbInverseElements() == 0 )
3448 aMesh->RemoveNode( N1[4] );
3449 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3450 aMesh->RemoveNode( N1[6] );
3451 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3452 aMesh->RemoveNode( N2[6] );
3456 // Next element to fuse: the rejected one
3458 startElem = Ok12 ? tr3 : tr2;
3460 } // if ( startElem )
3461 } // while ( startElem || !startLinks.empty() )
3462 } // while ( ! mapEl_setLi.empty() )
3467 //================================================================================
3469 * \brief Return nodes linked to the given one
3470 * \param theNode - the node
3471 * \param linkedNodes - the found nodes
3472 * \param type - the type of elements to check
3474 * Medium nodes are ignored
3476 //================================================================================
3478 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3479 TIDSortedElemSet & linkedNodes,
3480 SMDSAbs_ElementType type )
3482 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3483 while ( elemIt->more() )
3485 const SMDS_MeshElement* elem = elemIt->next();
3486 if(elem->GetType() == SMDSAbs_0DElement)
3489 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3490 if ( elem->GetType() == SMDSAbs_Volume )
3492 SMDS_VolumeTool vol( elem );
3493 while ( nodeIt->more() ) {
3494 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3495 if ( theNode != n && vol.IsLinked( theNode, n ))
3496 linkedNodes.insert( n );
3501 for ( int i = 0; nodeIt->more(); ++i ) {
3502 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3503 if ( n == theNode ) {
3504 int iBefore = i - 1;
3506 if ( elem->IsQuadratic() ) {
3507 int nb = elem->NbNodes() / 2;
3508 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3509 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3511 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3512 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3519 //=======================================================================
3520 //function : laplacianSmooth
3521 //purpose : pulls theNode toward the center of surrounding nodes directly
3522 // connected to that node along an element edge
3523 //=======================================================================
3525 void laplacianSmooth(const SMDS_MeshNode* theNode,
3526 const Handle(Geom_Surface)& theSurface,
3527 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3529 // find surrounding nodes
3531 TIDSortedElemSet nodeSet;
3532 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3534 // compute new coodrs
3536 double coord[] = { 0., 0., 0. };
3537 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3538 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3539 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3540 if ( theSurface.IsNull() ) { // smooth in 3D
3541 coord[0] += node->X();
3542 coord[1] += node->Y();
3543 coord[2] += node->Z();
3545 else { // smooth in 2D
3546 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3547 gp_XY* uv = theUVMap[ node ];
3548 coord[0] += uv->X();
3549 coord[1] += uv->Y();
3552 int nbNodes = nodeSet.size();
3555 coord[0] /= nbNodes;
3556 coord[1] /= nbNodes;
3558 if ( !theSurface.IsNull() ) {
3559 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3560 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3561 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3567 coord[2] /= nbNodes;
3571 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3574 //=======================================================================
3575 //function : centroidalSmooth
3576 //purpose : pulls theNode toward the element-area-weighted centroid of the
3577 // surrounding elements
3578 //=======================================================================
3580 void centroidalSmooth(const SMDS_MeshNode* theNode,
3581 const Handle(Geom_Surface)& theSurface,
3582 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3584 gp_XYZ aNewXYZ(0.,0.,0.);
3585 SMESH::Controls::Area anAreaFunc;
3586 double totalArea = 0.;
3591 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3592 while ( elemIt->more() )
3594 const SMDS_MeshElement* elem = elemIt->next();
3597 gp_XYZ elemCenter(0.,0.,0.);
3598 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3599 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3600 int nn = elem->NbNodes();
3601 if(elem->IsQuadratic()) nn = nn/2;
3603 //while ( itN->more() ) {
3605 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3607 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3608 aNodePoints.push_back( aP );
3609 if ( !theSurface.IsNull() ) { // smooth in 2D
3610 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3611 gp_XY* uv = theUVMap[ aNode ];
3612 aP.SetCoord( uv->X(), uv->Y(), 0. );
3616 double elemArea = anAreaFunc.GetValue( aNodePoints );
3617 totalArea += elemArea;
3619 aNewXYZ += elemCenter * elemArea;
3621 aNewXYZ /= totalArea;
3622 if ( !theSurface.IsNull() ) {
3623 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3624 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3629 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3632 //=======================================================================
3633 //function : getClosestUV
3634 //purpose : return UV of closest projection
3635 //=======================================================================
3637 static bool getClosestUV (Extrema_GenExtPS& projector,
3638 const gp_Pnt& point,
3641 projector.Perform( point );
3642 if ( projector.IsDone() ) {
3643 double u = 0, v = 0, minVal = DBL_MAX;
3644 for ( int i = projector.NbExt(); i > 0; i-- )
3645 if ( projector.SquareDistance( i ) < minVal ) {
3646 minVal = projector.SquareDistance( i );
3647 projector.Point( i ).Parameter( u, v );
3649 result.SetCoord( u, v );
3655 //=======================================================================
3657 //purpose : Smooth theElements during theNbIterations or until a worst
3658 // element has aspect ratio <= theTgtAspectRatio.
3659 // Aspect Ratio varies in range [1.0, inf].
3660 // If theElements is empty, the whole mesh is smoothed.
3661 // theFixedNodes contains additionally fixed nodes. Nodes built
3662 // on edges and boundary nodes are always fixed.
3663 //=======================================================================
3665 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3666 set<const SMDS_MeshNode*> & theFixedNodes,
3667 const SmoothMethod theSmoothMethod,
3668 const int theNbIterations,
3669 double theTgtAspectRatio,
3674 if ( theTgtAspectRatio < 1.0 )
3675 theTgtAspectRatio = 1.0;
3677 const double disttol = 1.e-16;
3679 SMESH::Controls::AspectRatio aQualityFunc;
3681 SMESHDS_Mesh* aMesh = GetMeshDS();
3683 if ( theElems.empty() ) {
3684 // add all faces to theElems
3685 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3686 while ( fIt->more() ) {
3687 const SMDS_MeshElement* face = fIt->next();
3688 theElems.insert( theElems.end(), face );
3691 // get all face ids theElems are on
3692 set< int > faceIdSet;
3693 TIDSortedElemSet::iterator itElem;
3695 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3696 int fId = FindShape( *itElem );
3697 // check that corresponding submesh exists and a shape is face
3699 faceIdSet.find( fId ) == faceIdSet.end() &&
3700 aMesh->MeshElements( fId )) {
3701 TopoDS_Shape F = aMesh->IndexToShape( fId );
3702 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3703 faceIdSet.insert( fId );
3706 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3708 // ===============================================
3709 // smooth elements on each TopoDS_Face separately
3710 // ===============================================
3712 SMESH_MesherHelper helper( *GetMesh() );
3714 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3715 for ( ; fId != faceIdSet.rend(); ++fId )
3717 // get face surface and submesh
3718 Handle(Geom_Surface) surface;
3719 SMESHDS_SubMesh* faceSubMesh = 0;
3722 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3723 bool isUPeriodic = false, isVPeriodic = false;
3726 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3727 surface = BRep_Tool::Surface( face );
3728 faceSubMesh = aMesh->MeshElements( *fId );
3729 fToler2 = BRep_Tool::Tolerance( face );
3730 fToler2 *= fToler2 * 10.;
3731 isUPeriodic = surface->IsUPeriodic();
3732 // if ( isUPeriodic )
3733 // surface->UPeriod();
3734 isVPeriodic = surface->IsVPeriodic();
3735 // if ( isVPeriodic )
3736 // surface->VPeriod();
3737 surface->Bounds( u1, u2, v1, v2 );
3738 helper.SetSubShape( face );
3740 // ---------------------------------------------------------
3741 // for elements on a face, find movable and fixed nodes and
3742 // compute UV for them
3743 // ---------------------------------------------------------
3744 bool checkBoundaryNodes = false;
3745 bool isQuadratic = false;
3746 set<const SMDS_MeshNode*> setMovableNodes;
3747 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3748 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3749 list< const SMDS_MeshElement* > elemsOnFace;
3751 Extrema_GenExtPS projector;
3752 GeomAdaptor_Surface surfAdaptor;
3753 if ( !surface.IsNull() ) {
3754 surfAdaptor.Load( surface );
3755 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3757 int nbElemOnFace = 0;
3758 itElem = theElems.begin();
3759 // loop on not yet smoothed elements: look for elems on a face
3760 while ( itElem != theElems.end() )
3762 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3763 break; // all elements found
3765 const SMDS_MeshElement* elem = *itElem;
3766 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3767 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3771 elemsOnFace.push_back( elem );
3772 theElems.erase( itElem++ );
3776 isQuadratic = elem->IsQuadratic();
3778 // get movable nodes of elem
3779 const SMDS_MeshNode* node;
3780 SMDS_TypeOfPosition posType;
3781 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3782 int nn = 0, nbn = elem->NbNodes();
3783 if(elem->IsQuadratic())
3785 while ( nn++ < nbn ) {
3786 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3787 const SMDS_PositionPtr& pos = node->GetPosition();
3788 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3789 if (posType != SMDS_TOP_EDGE &&
3790 posType != SMDS_TOP_VERTEX &&
3791 theFixedNodes.find( node ) == theFixedNodes.end())
3793 // check if all faces around the node are on faceSubMesh
3794 // because a node on edge may be bound to face
3796 if ( faceSubMesh ) {
3797 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3798 while ( eIt->more() && all ) {
3799 const SMDS_MeshElement* e = eIt->next();
3800 all = faceSubMesh->Contains( e );
3804 setMovableNodes.insert( node );
3806 checkBoundaryNodes = true;
3808 if ( posType == SMDS_TOP_3DSPACE )
3809 checkBoundaryNodes = true;
3812 if ( surface.IsNull() )
3815 // get nodes to check UV
3816 list< const SMDS_MeshNode* > uvCheckNodes;
3817 const SMDS_MeshNode* nodeInFace = 0;
3818 itN = elem->nodesIterator();
3819 nn = 0; nbn = elem->NbNodes();
3820 if(elem->IsQuadratic())
3822 while ( nn++ < nbn ) {
3823 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3824 if ( node->GetPosition()->GetDim() == 2 )
3826 if ( uvMap.find( node ) == uvMap.end() )
3827 uvCheckNodes.push_back( node );
3828 // add nodes of elems sharing node
3829 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3830 // while ( eIt->more() ) {
3831 // const SMDS_MeshElement* e = eIt->next();
3832 // if ( e != elem ) {
3833 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3834 // while ( nIt->more() ) {
3835 // const SMDS_MeshNode* n =
3836 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3837 // if ( uvMap.find( n ) == uvMap.end() )
3838 // uvCheckNodes.push_back( n );
3844 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3845 for ( ; n != uvCheckNodes.end(); ++n ) {
3848 const SMDS_PositionPtr& pos = node->GetPosition();
3849 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3853 bool toCheck = true;
3854 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3856 // compute not existing UV
3857 bool project = ( posType == SMDS_TOP_3DSPACE );
3858 // double dist1 = DBL_MAX, dist2 = 0;
3859 // if ( posType != SMDS_TOP_3DSPACE ) {
3860 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3861 // project = dist1 > fToler2;
3863 if ( project ) { // compute new UV
3865 gp_Pnt pNode = SMESH_NodeXYZ( node );
3866 if ( !getClosestUV( projector, pNode, newUV )) {
3867 MESSAGE("Node Projection Failed " << node);
3871 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3873 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3875 // if ( posType != SMDS_TOP_3DSPACE )
3876 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3877 // if ( dist2 < dist1 )
3881 // store UV in the map
3882 listUV.push_back( uv );
3883 uvMap.insert( make_pair( node, &listUV.back() ));
3885 } // loop on not yet smoothed elements
3887 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3888 checkBoundaryNodes = true;
3890 // fix nodes on mesh boundary
3892 if ( checkBoundaryNodes ) {
3893 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3894 map< SMESH_TLink, int >::iterator link_nb;
3895 // put all elements links to linkNbMap
3896 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3897 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3898 const SMDS_MeshElement* elem = (*elemIt);
3899 int nbn = elem->NbCornerNodes();
3900 // loop on elem links: insert them in linkNbMap
3901 for ( int iN = 0; iN < nbn; ++iN ) {
3902 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3903 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3904 SMESH_TLink link( n1, n2 );
3905 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3909 // remove nodes that are in links encountered only once from setMovableNodes
3910 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3911 if ( link_nb->second == 1 ) {
3912 setMovableNodes.erase( link_nb->first.node1() );
3913 setMovableNodes.erase( link_nb->first.node2() );
3918 // -----------------------------------------------------
3919 // for nodes on seam edge, compute one more UV ( uvMap2 );
3920 // find movable nodes linked to nodes on seam and which
3921 // are to be smoothed using the second UV ( uvMap2 )
3922 // -----------------------------------------------------
3924 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3925 if ( !surface.IsNull() ) {
3926 TopExp_Explorer eExp( face, TopAbs_EDGE );
3927 for ( ; eExp.More(); eExp.Next() ) {
3928 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3929 if ( !BRep_Tool::IsClosed( edge, face ))
3931 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3932 if ( !sm ) continue;
3933 // find out which parameter varies for a node on seam
3936 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3937 if ( pcurve.IsNull() ) continue;
3938 uv1 = pcurve->Value( f );
3940 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941 if ( pcurve.IsNull() ) continue;
3942 uv2 = pcurve->Value( f );
3943 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3945 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3946 std::swap( uv1, uv2 );
3947 // get nodes on seam and its vertices
3948 list< const SMDS_MeshNode* > seamNodes;
3949 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3950 while ( nSeamIt->more() ) {
3951 const SMDS_MeshNode* node = nSeamIt->next();
3952 if ( !isQuadratic || !IsMedium( node ))
3953 seamNodes.push_back( node );
3955 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3956 for ( ; vExp.More(); vExp.Next() ) {
3957 sm = aMesh->MeshElements( vExp.Current() );
3959 nSeamIt = sm->GetNodes();
3960 while ( nSeamIt->more() )
3961 seamNodes.push_back( nSeamIt->next() );
3964 // loop on nodes on seam
3965 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3966 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3967 const SMDS_MeshNode* nSeam = *noSeIt;
3968 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3969 if ( n_uv == uvMap.end() )
3972 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3973 // set the second UV
3974 listUV.push_back( *n_uv->second );
3975 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3976 if ( uvMap2.empty() )
3977 uvMap2 = uvMap; // copy the uvMap contents
3978 uvMap2[ nSeam ] = &listUV.back();
3980 // collect movable nodes linked to ones on seam in nodesNearSeam
3981 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3982 while ( eIt->more() ) {
3983 const SMDS_MeshElement* e = eIt->next();
3984 int nbUseMap1 = 0, nbUseMap2 = 0;
3985 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3986 int nn = 0, nbn = e->NbNodes();
3987 if(e->IsQuadratic()) nbn = nbn/2;
3988 while ( nn++ < nbn )
3990 const SMDS_MeshNode* n =
3991 static_cast<const SMDS_MeshNode*>( nIt->next() );
3993 setMovableNodes.find( n ) == setMovableNodes.end() )
3995 // add only nodes being closer to uv2 than to uv1
3996 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3997 // 0.5 * ( n->Y() + nSeam->Y() ),
3998 // 0.5 * ( n->Z() + nSeam->Z() ));
4000 // getClosestUV( projector, pMid, uv );
4001 double x = uvMap[ n ]->Coord( iPar );
4002 if ( Abs( uv1.Coord( iPar ) - x ) >
4003 Abs( uv2.Coord( iPar ) - x )) {
4004 nodesNearSeam.insert( n );
4010 // for centroidalSmooth all element nodes must
4011 // be on one side of a seam
4012 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4013 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4015 while ( nn++ < nbn ) {
4016 const SMDS_MeshNode* n =
4017 static_cast<const SMDS_MeshNode*>( nIt->next() );
4018 setMovableNodes.erase( n );
4022 } // loop on nodes on seam
4023 } // loop on edge of a face
4024 } // if ( !face.IsNull() )
4026 if ( setMovableNodes.empty() ) {
4027 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4028 continue; // goto next face
4036 double maxRatio = -1., maxDisplacement = -1.;
4037 set<const SMDS_MeshNode*>::iterator nodeToMove;
4038 for ( it = 0; it < theNbIterations; it++ ) {
4039 maxDisplacement = 0.;
4040 nodeToMove = setMovableNodes.begin();
4041 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4042 const SMDS_MeshNode* node = (*nodeToMove);
4043 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4046 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4047 if ( theSmoothMethod == LAPLACIAN )
4048 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4050 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4052 // node displacement
4053 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4054 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4055 if ( aDispl > maxDisplacement )
4056 maxDisplacement = aDispl;
4058 // no node movement => exit
4059 //if ( maxDisplacement < 1.e-16 ) {
4060 if ( maxDisplacement < disttol ) {
4061 MESSAGE("-- no node movement --");
4065 // check elements quality
4067 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4068 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4069 const SMDS_MeshElement* elem = (*elemIt);
4070 if ( !elem || elem->GetType() != SMDSAbs_Face )
4072 SMESH::Controls::TSequenceOfXYZ aPoints;
4073 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4074 double aValue = aQualityFunc.GetValue( aPoints );
4075 if ( aValue > maxRatio )
4079 if ( maxRatio <= theTgtAspectRatio ) {
4080 //MESSAGE("-- quality achieved --");
4083 if (it+1 == theNbIterations) {
4084 //MESSAGE("-- Iteration limit exceeded --");
4086 } // smoothing iterations
4088 // MESSAGE(" Face id: " << *fId <<
4089 // " Nb iterstions: " << it <<
4090 // " Displacement: " << maxDisplacement <<
4091 // " Aspect Ratio " << maxRatio);
4093 // ---------------------------------------
4094 // new nodes positions are computed,
4095 // record movement in DS and set new UV
4096 // ---------------------------------------
4097 nodeToMove = setMovableNodes.begin();
4098 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4099 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4100 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4101 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4102 if ( node_uv != uvMap.end() ) {
4103 gp_XY* uv = node_uv->second;
4105 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4109 // move medium nodes of quadratic elements
4112 vector<const SMDS_MeshNode*> nodes;
4114 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4115 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4117 const SMDS_MeshElement* QF = *elemIt;
4118 if ( QF->IsQuadratic() )
4120 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4121 SMDS_MeshElement::iterator() );
4122 nodes.push_back( nodes[0] );
4124 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4126 if ( !surface.IsNull() )
4128 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4129 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4130 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4131 xyz = surface->Value( uv.X(), uv.Y() );
4134 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4136 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4137 // we have to move a medium node
4138 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4144 } // loop on face ids
4150 //=======================================================================
4151 //function : isReverse
4152 //purpose : Return true if normal of prevNodes is not co-directied with
4153 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4154 // iNotSame is where prevNodes and nextNodes are different.
4155 // If result is true then future volume orientation is OK
4156 //=======================================================================
4158 bool isReverse(const SMDS_MeshElement* face,
4159 const vector<const SMDS_MeshNode*>& prevNodes,
4160 const vector<const SMDS_MeshNode*>& nextNodes,
4164 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4165 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4166 gp_XYZ extrDir( pN - pP ), faceNorm;
4167 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4169 return faceNorm * extrDir < 0.0;
4172 //================================================================================
4174 * \brief Assure that theElemSets[0] holds elements, not nodes
4176 //================================================================================
4178 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4180 if ( !theElemSets[0].empty() &&
4181 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4183 std::swap( theElemSets[0], theElemSets[1] );
4185 else if ( !theElemSets[1].empty() &&
4186 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4188 std::swap( theElemSets[0], theElemSets[1] );
4193 //=======================================================================
4195 * \brief Create elements by sweeping an element
4196 * \param elem - element to sweep
4197 * \param newNodesItVec - nodes generated from each node of the element
4198 * \param newElems - generated elements
4199 * \param nbSteps - number of sweeping steps
4200 * \param srcElements - to append elem for each generated element
4202 //=======================================================================
4204 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4205 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4206 list<const SMDS_MeshElement*>& newElems,
4207 const size_t nbSteps,
4208 SMESH_SequenceOfElemPtr& srcElements)
4210 SMESHDS_Mesh* aMesh = GetMeshDS();
4212 const int nbNodes = elem->NbNodes();
4213 const int nbCorners = elem->NbCornerNodes();
4214 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4215 polyhedron creation !!! */
4216 // Loop on elem nodes:
4217 // find new nodes and detect same nodes indices
4218 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4219 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4220 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4221 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4223 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4224 vector<int> sames(nbNodes);
4225 vector<bool> isSingleNode(nbNodes);
4227 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4228 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4229 const SMDS_MeshNode* node = nnIt->first;
4230 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4231 if ( listNewNodes.empty() )
4234 itNN [ iNode ] = listNewNodes.begin();
4235 prevNod[ iNode ] = node;
4236 nextNod[ iNode ] = listNewNodes.front();
4238 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4239 corner node of linear */
4240 if ( prevNod[ iNode ] != nextNod [ iNode ])
4241 nbDouble += !isSingleNode[iNode];
4243 if( iNode < nbCorners ) { // check corners only
4244 if ( prevNod[ iNode ] == nextNod [ iNode ])
4245 sames[nbSame++] = iNode;
4247 iNotSameNode = iNode;
4251 if ( nbSame == nbNodes || nbSame > 2) {
4252 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4256 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4258 // fix nodes order to have bottom normal external
4259 if ( baseType == SMDSEntity_Polygon )
4261 std::reverse( itNN.begin(), itNN.end() );
4262 std::reverse( prevNod.begin(), prevNod.end() );
4263 std::reverse( midlNod.begin(), midlNod.end() );
4264 std::reverse( nextNod.begin(), nextNod.end() );
4265 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4269 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4270 SMDS_MeshCell::applyInterlace( ind, itNN );
4271 SMDS_MeshCell::applyInterlace( ind, prevNod );
4272 SMDS_MeshCell::applyInterlace( ind, nextNod );
4273 SMDS_MeshCell::applyInterlace( ind, midlNod );
4274 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4277 sames[nbSame] = iNotSameNode;
4278 for ( int j = 0; j <= nbSame; ++j )
4279 for ( size_t i = 0; i < ind.size(); ++i )
4280 if ( ind[i] == sames[j] )
4285 iNotSameNode = sames[nbSame];
4289 else if ( elem->GetType() == SMDSAbs_Edge )
4291 // orient a new face same as adjacent one
4293 const SMDS_MeshElement* e;
4294 TIDSortedElemSet dummy;
4295 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4296 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4297 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4299 // there is an adjacent face, check order of nodes in it
4300 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4303 std::swap( itNN[0], itNN[1] );
4304 std::swap( prevNod[0], prevNod[1] );
4305 std::swap( nextNod[0], nextNod[1] );
4306 std::swap( isSingleNode[0], isSingleNode[1] );
4308 sames[0] = 1 - sames[0];
4309 iNotSameNode = 1 - iNotSameNode;
4314 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4316 iSameNode = sames[ nbSame-1 ];
4317 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4318 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4319 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4322 if ( baseType == SMDSEntity_Polygon )
4324 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4325 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4327 else if ( baseType == SMDSEntity_Quad_Polygon )
4329 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4330 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4333 // make new elements
4334 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4337 for ( iNode = 0; iNode < nbNodes; iNode++ )
4339 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4340 nextNod[ iNode ] = *itNN[ iNode ]++;
4343 SMDS_MeshElement* aNewElem = 0;
4344 /*if(!elem->IsPoly())*/ {
4345 switch ( baseType ) {
4347 case SMDSEntity_Node: { // sweep NODE
4348 if ( nbSame == 0 ) {
4349 if ( isSingleNode[0] )
4350 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4352 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4358 case SMDSEntity_Edge: { // sweep EDGE
4359 if ( nbDouble == 0 )
4361 if ( nbSame == 0 ) // ---> quadrangle
4362 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4363 nextNod[ 1 ], nextNod[ 0 ] );
4364 else // ---> triangle
4365 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4366 nextNod[ iNotSameNode ] );
4368 else // ---> polygon
4370 vector<const SMDS_MeshNode*> poly_nodes;
4371 poly_nodes.push_back( prevNod[0] );
4372 poly_nodes.push_back( prevNod[1] );
4373 if ( prevNod[1] != nextNod[1] )
4375 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4376 poly_nodes.push_back( nextNod[1] );
4378 if ( prevNod[0] != nextNod[0] )
4380 poly_nodes.push_back( nextNod[0] );
4381 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4383 switch ( poly_nodes.size() ) {
4385 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4388 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4389 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4392 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4397 case SMDSEntity_Triangle: // TRIANGLE --->
4399 if ( nbDouble > 0 ) break;
4400 if ( nbSame == 0 ) // ---> pentahedron
4401 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4402 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4404 else if ( nbSame == 1 ) // ---> pyramid
4405 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4406 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4407 nextNod[ iSameNode ]);
4409 else // 2 same nodes: ---> tetrahedron
4410 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4411 nextNod[ iNotSameNode ]);
4414 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4418 if ( nbDouble+nbSame == 2 )
4420 if(nbSame==0) { // ---> quadratic quadrangle
4421 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4422 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4424 else { //(nbSame==1) // ---> quadratic triangle
4426 return; // medium node on axis
4428 else if(sames[0]==0)
4429 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4430 prevNod[2], midlNod[1], nextNod[2] );
4432 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4433 prevNod[2], nextNod[2], midlNod[0]);
4436 else if ( nbDouble == 3 )
4438 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4439 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4440 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4447 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4448 if ( nbDouble > 0 ) break;
4450 if ( nbSame == 0 ) // ---> hexahedron
4451 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4452 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4454 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4455 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4456 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4457 nextNod[ iSameNode ]);
4458 newElems.push_back( aNewElem );
4459 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4460 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4461 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4463 else if ( nbSame == 2 ) { // ---> pentahedron
4464 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4465 // iBeforeSame is same too
4466 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4467 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4468 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4470 // iAfterSame is same too
4471 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4472 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4473 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4477 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4478 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4479 if ( nbDouble+nbSame != 3 ) break;
4481 // ---> pentahedron with 15 nodes
4482 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4483 nextNod[0], nextNod[1], nextNod[2],
4484 prevNod[3], prevNod[4], prevNod[5],
4485 nextNod[3], nextNod[4], nextNod[5],
4486 midlNod[0], midlNod[1], midlNod[2]);
4488 else if(nbSame==1) {
4489 // ---> 2d order pyramid of 13 nodes
4490 int apex = iSameNode;
4491 int i0 = ( apex + 1 ) % nbCorners;
4492 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4496 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4497 nextNod[i0], nextNod[i1], prevNod[apex],
4498 prevNod[i01], midlNod[i0],
4499 nextNod[i01], midlNod[i1],
4500 prevNod[i1a], prevNod[i0a],
4501 nextNod[i0a], nextNod[i1a]);
4503 else if(nbSame==2) {
4504 // ---> 2d order tetrahedron of 10 nodes
4505 int n1 = iNotSameNode;
4506 int n2 = ( n1 + 1 ) % nbCorners;
4507 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4511 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4512 prevNod[n12], prevNod[n23], prevNod[n31],
4513 midlNod[n1], nextNod[n12], nextNod[n31]);
4517 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4519 if ( nbDouble != 4 ) break;
4520 // ---> hexahedron with 20 nodes
4521 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4522 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4523 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4524 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4525 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4527 else if(nbSame==1) {
4528 // ---> pyramid + pentahedron - can not be created since it is needed
4529 // additional middle node at the center of face
4530 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4533 else if( nbSame == 2 ) {
4534 if ( nbDouble != 2 ) break;
4535 // ---> 2d order Pentahedron with 15 nodes
4537 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4538 // iBeforeSame is same too
4545 // iAfterSame is same too
4555 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4556 prevNod[n4], prevNod[n5], nextNod[n5],
4557 prevNod[n12], midlNod[n2], nextNod[n12],
4558 prevNod[n45], midlNod[n5], nextNod[n45],
4559 prevNod[n14], prevNod[n25], nextNod[n25]);
4563 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4565 if( nbSame == 0 && nbDouble == 9 ) {
4566 // ---> tri-quadratic hexahedron with 27 nodes
4567 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4568 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4569 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4570 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4571 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4572 prevNod[8], // bottom center
4573 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4574 nextNod[8], // top center
4575 midlNod[8]);// elem center
4583 case SMDSEntity_Polygon: { // sweep POLYGON
4585 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4586 // ---> hexagonal prism
4587 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4588 prevNod[3], prevNod[4], prevNod[5],
4589 nextNod[0], nextNod[1], nextNod[2],
4590 nextNod[3], nextNod[4], nextNod[5]);
4594 case SMDSEntity_Ball:
4599 } // switch ( baseType )
4602 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4604 if ( baseType != SMDSEntity_Polygon )
4606 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4607 SMDS_MeshCell::applyInterlace( ind, prevNod );
4608 SMDS_MeshCell::applyInterlace( ind, nextNod );
4609 SMDS_MeshCell::applyInterlace( ind, midlNod );
4610 SMDS_MeshCell::applyInterlace( ind, itNN );
4611 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4612 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4614 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4615 vector<int> quantities (nbNodes + 2);
4616 polyedre_nodes.clear();
4620 for (int inode = 0; inode < nbNodes; inode++)
4621 polyedre_nodes.push_back( prevNod[inode] );
4622 quantities.push_back( nbNodes );
4625 polyedre_nodes.push_back( nextNod[0] );
4626 for (int inode = nbNodes; inode-1; --inode )
4627 polyedre_nodes.push_back( nextNod[inode-1] );
4628 quantities.push_back( nbNodes );
4636 const int iQuad = elem->IsQuadratic();
4637 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4639 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4640 int inextface = (iface+1+iQuad) % nbNodes;
4641 int imid = (iface+1) % nbNodes;
4642 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4643 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4644 polyedre_nodes.push_back( prevNod[iface] ); // 1
4645 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4647 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4648 polyedre_nodes.push_back( nextNod[iface] ); // 2
4650 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4651 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4653 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4654 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4656 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4657 if ( nbFaceNodes > 2 )
4658 quantities.push_back( nbFaceNodes );
4659 else // degenerated face
4660 polyedre_nodes.resize( prevNbNodes );
4662 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4664 } // try to create a polyherdal prism
4667 newElems.push_back( aNewElem );
4668 myLastCreatedElems.push_back(aNewElem);
4669 srcElements.push_back( elem );
4672 // set new prev nodes
4673 for ( iNode = 0; iNode < nbNodes; iNode++ )
4674 prevNod[ iNode ] = nextNod[ iNode ];
4679 //=======================================================================
4681 * \brief Create 1D and 2D elements around swept elements
4682 * \param mapNewNodes - source nodes and ones generated from them
4683 * \param newElemsMap - source elements and ones generated from them
4684 * \param elemNewNodesMap - nodes generated from each node of each element
4685 * \param elemSet - all swept elements
4686 * \param nbSteps - number of sweeping steps
4687 * \param srcElements - to append elem for each generated element
4689 //=======================================================================
4691 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4692 TTElemOfElemListMap & newElemsMap,
4693 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4694 TIDSortedElemSet& elemSet,
4696 SMESH_SequenceOfElemPtr& srcElements)
4698 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4699 SMESHDS_Mesh* aMesh = GetMeshDS();
4701 // Find nodes belonging to only one initial element - sweep them into edges.
4703 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4704 for ( ; nList != mapNewNodes.end(); nList++ )
4706 const SMDS_MeshNode* node =
4707 static_cast<const SMDS_MeshNode*>( nList->first );
4708 if ( newElemsMap.count( node ))
4709 continue; // node was extruded into edge
4710 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4711 int nbInitElems = 0;
4712 const SMDS_MeshElement* el = 0;
4713 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4714 while ( eIt->more() && nbInitElems < 2 ) {
4715 const SMDS_MeshElement* e = eIt->next();
4716 SMDSAbs_ElementType type = e->GetType();
4717 if ( type == SMDSAbs_Volume ||
4721 if ( type > highType ) {
4728 if ( nbInitElems == 1 ) {
4729 bool NotCreateEdge = el && el->IsMediumNode(node);
4730 if(!NotCreateEdge) {
4731 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4732 list<const SMDS_MeshElement*> newEdges;
4733 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4738 // Make a ceiling for each element ie an equal element of last new nodes.
4739 // Find free links of faces - make edges and sweep them into faces.
4741 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4743 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4744 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4745 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4747 const SMDS_MeshElement* elem = itElem->first;
4748 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4750 if(itElem->second.size()==0) continue;
4752 const bool isQuadratic = elem->IsQuadratic();
4754 if ( elem->GetType() == SMDSAbs_Edge ) {
4755 // create a ceiling edge
4756 if ( !isQuadratic ) {
4757 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4758 vecNewNodes[ 1 ]->second.back())) {
4759 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4760 vecNewNodes[ 1 ]->second.back()));
4761 srcElements.push_back( elem );
4765 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4766 vecNewNodes[ 1 ]->second.back(),
4767 vecNewNodes[ 2 ]->second.back())) {
4768 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4769 vecNewNodes[ 1 ]->second.back(),
4770 vecNewNodes[ 2 ]->second.back()));
4771 srcElements.push_back( elem );
4775 if ( elem->GetType() != SMDSAbs_Face )
4778 bool hasFreeLinks = false;
4780 TIDSortedElemSet avoidSet;
4781 avoidSet.insert( elem );
4783 set<const SMDS_MeshNode*> aFaceLastNodes;
4784 int iNode, nbNodes = vecNewNodes.size();
4785 if ( !isQuadratic ) {
4786 // loop on the face nodes
4787 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4788 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4789 // look for free links of the face
4790 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4791 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4792 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4793 // check if a link n1-n2 is free
4794 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4795 hasFreeLinks = true;
4796 // make a new edge and a ceiling for a new edge
4797 const SMDS_MeshElement* edge;
4798 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4799 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4800 srcElements.push_back( myLastCreatedElems.back() );
4802 n1 = vecNewNodes[ iNode ]->second.back();
4803 n2 = vecNewNodes[ iNext ]->second.back();
4804 if ( !aMesh->FindEdge( n1, n2 )) {
4805 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4806 srcElements.push_back( edge );
4811 else { // elem is quadratic face
4812 int nbn = nbNodes/2;
4813 for ( iNode = 0; iNode < nbn; iNode++ ) {
4814 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4815 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4816 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4817 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4818 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4819 // check if a link is free
4820 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4821 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4822 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4823 hasFreeLinks = true;
4824 // make an edge and a ceiling for a new edge
4826 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4827 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4828 srcElements.push_back( elem );
4830 n1 = vecNewNodes[ iNode ]->second.back();
4831 n2 = vecNewNodes[ iNext ]->second.back();
4832 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4833 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4834 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4835 srcElements.push_back( elem );
4839 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4840 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4844 // sweep free links into faces
4846 if ( hasFreeLinks ) {
4847 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4848 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4850 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4851 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4852 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4853 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4854 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4856 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4857 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4858 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4860 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4861 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4862 std::advance( v, volNb );
4863 // find indices of free faces of a volume and their source edges
4864 list< int > freeInd;
4865 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4866 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4867 int iF, nbF = vTool.NbFaces();
4868 for ( iF = 0; iF < nbF; iF ++ ) {
4869 if ( vTool.IsFreeFace( iF ) &&
4870 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4871 initNodeSet != faceNodeSet) // except an initial face
4873 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4875 if ( faceNodeSet == initNodeSetNoCenter )
4877 freeInd.push_back( iF );
4878 // find source edge of a free face iF
4879 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4880 vector<const SMDS_MeshNode*>::iterator lastCommom;
4881 commonNodes.resize( nbNodes, 0 );
4882 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4883 initNodeSet.begin(), initNodeSet.end(),
4884 commonNodes.begin());
4885 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4886 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4888 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4890 if ( !srcEdges.back() )
4892 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4893 << iF << " of volume #" << vTool.ID() << endl;
4898 if ( freeInd.empty() )
4901 // create wall faces for all steps;
4902 // if such a face has been already created by sweep of edge,
4903 // assure that its orientation is OK
4904 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4906 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4907 vTool.SetExternalNormal();
4908 const int nextShift = vTool.IsForward() ? +1 : -1;
4909 list< int >::iterator ind = freeInd.begin();
4910 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4911 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4913 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4914 int nbn = vTool.NbFaceNodes( *ind );
4915 const SMDS_MeshElement * f = 0;
4916 if ( nbn == 3 ) ///// triangle
4918 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4920 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4922 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4924 nodes[ 1 + nextShift ] };
4926 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4928 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4932 else if ( nbn == 4 ) ///// quadrangle
4934 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4936 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4938 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4939 nodes[ 2 ], nodes[ 2+nextShift ] };
4941 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4943 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4944 newOrder[ 2 ], newOrder[ 3 ]));
4947 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4949 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4951 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4953 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4955 nodes[2 + 2*nextShift],
4956 nodes[3 - 2*nextShift],
4958 nodes[3 + 2*nextShift]};
4960 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4962 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4970 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4972 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4973 nodes[1], nodes[3], nodes[5], nodes[7] );
4975 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4977 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4978 nodes[4 - 2*nextShift],
4980 nodes[4 + 2*nextShift],
4982 nodes[5 - 2*nextShift],
4984 nodes[5 + 2*nextShift] };
4986 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4988 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4989 newOrder[ 2 ], newOrder[ 3 ],
4990 newOrder[ 4 ], newOrder[ 5 ],
4991 newOrder[ 6 ], newOrder[ 7 ]));
4994 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4996 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4997 SMDSAbs_Face, /*noMedium=*/false);
4999 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5001 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5002 nodes[4 - 2*nextShift],
5004 nodes[4 + 2*nextShift],
5006 nodes[5 - 2*nextShift],
5008 nodes[5 + 2*nextShift],
5011 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5013 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5014 newOrder[ 2 ], newOrder[ 3 ],
5015 newOrder[ 4 ], newOrder[ 5 ],
5016 newOrder[ 6 ], newOrder[ 7 ],
5020 else //////// polygon
5022 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5023 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5025 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5027 if ( !vTool.IsForward() )
5028 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5030 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5032 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5036 while ( srcElements.size() < myLastCreatedElems.size() )
5037 srcElements.push_back( *srcEdge );
5039 } // loop on free faces
5041 // go to the next volume
5043 while ( iVol++ < nbVolumesByStep ) v++;
5046 } // loop on volumes of one step
5047 } // sweep free links into faces
5049 // Make a ceiling face with a normal external to a volume
5051 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5052 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5053 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5055 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5056 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5057 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5061 lastVol.SetExternalNormal();
5062 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5063 const int nbn = lastVol.NbFaceNodes( iF );
5064 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5065 if ( !hasFreeLinks ||
5066 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5068 const vector<int>& interlace =
5069 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5070 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5072 AddElement( nodeVec, anyFace.Init( elem ));
5074 while ( srcElements.size() < myLastCreatedElems.size() )
5075 srcElements.push_back( elem );
5078 } // loop on swept elements
5081 //=======================================================================
5082 //function : RotationSweep
5084 //=======================================================================
5086 SMESH_MeshEditor::PGroupIDs
5087 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5088 const gp_Ax1& theAxis,
5089 const double theAngle,
5090 const int theNbSteps,
5091 const double theTol,
5092 const bool theMakeGroups,
5093 const bool theMakeWalls)
5097 setElemsFirst( theElemSets );
5098 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5099 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5101 // source elements for each generated one
5102 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5103 srcElems.reserve( theElemSets[0].size() );
5104 srcNodes.reserve( theElemSets[1].size() );
5107 aTrsf.SetRotation( theAxis, theAngle );
5109 aTrsf2.SetRotation( theAxis, theAngle/2. );
5111 gp_Lin aLine( theAxis );
5112 double aSqTol = theTol * theTol;
5114 SMESHDS_Mesh* aMesh = GetMeshDS();
5116 TNodeOfNodeListMap mapNewNodes;
5117 TElemOfVecOfNnlmiMap mapElemNewNodes;
5118 TTElemOfElemListMap newElemsMap;
5120 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5121 myMesh->NbFaces(ORDER_QUADRATIC) +
5122 myMesh->NbVolumes(ORDER_QUADRATIC) );
5123 // loop on theElemSets
5124 TIDSortedElemSet::iterator itElem;
5125 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5127 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5128 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5129 const SMDS_MeshElement* elem = *itElem;
5130 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5132 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5133 newNodesItVec.reserve( elem->NbNodes() );
5135 // loop on elem nodes
5136 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5137 while ( itN->more() )
5139 const SMDS_MeshNode* node = cast2Node( itN->next() );
5141 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5143 aXYZ.Coord( coord[0], coord[1], coord[2] );
5144 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5146 // check if a node has been already sweeped
5147 TNodeOfNodeListMapItr nIt =
5148 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5149 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5150 if ( listNewNodes.empty() )
5152 // check if we are to create medium nodes between corner ones
5153 bool needMediumNodes = false;
5154 if ( isQuadraticMesh )
5156 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5157 while (it->more() && !needMediumNodes )
5159 const SMDS_MeshElement* invElem = it->next();
5160 if ( invElem != elem && !theElems.count( invElem )) continue;
5161 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5162 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5163 needMediumNodes = true;
5168 const SMDS_MeshNode * newNode = node;
5169 for ( int i = 0; i < theNbSteps; i++ ) {
5171 if ( needMediumNodes ) // create a medium node
5173 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5174 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5175 myLastCreatedNodes.push_back(newNode);
5176 srcNodes.push_back( node );
5177 listNewNodes.push_back( newNode );
5178 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5181 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5183 // create a corner node
5184 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5185 myLastCreatedNodes.push_back(newNode);
5186 srcNodes.push_back( node );
5187 listNewNodes.push_back( newNode );
5190 listNewNodes.push_back( newNode );
5191 // if ( needMediumNodes )
5192 // listNewNodes.push_back( newNode );
5196 newNodesItVec.push_back( nIt );
5198 // make new elements
5199 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5204 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5206 PGroupIDs newGroupIDs;
5207 if ( theMakeGroups )
5208 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5213 //=======================================================================
5214 //function : ExtrusParam
5215 //purpose : standard construction
5216 //=======================================================================
5218 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5219 const int theNbSteps,
5220 const std::list<double>& theScales,
5221 const std::list<double>& theAngles,
5222 const gp_XYZ* theBasePoint,
5224 const double theTolerance):
5226 myBaseP( Precision::Infinite(), 0, 0 ),
5227 myFlags( theFlags ),
5228 myTolerance( theTolerance ),
5229 myElemsToUse( NULL )
5231 mySteps = new TColStd_HSequenceOfReal;
5232 const double stepSize = theStep.Magnitude();
5233 for (int i=1; i<=theNbSteps; i++ )
5234 mySteps->Append( stepSize );
5236 if ( !theScales.empty() )
5238 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5239 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5241 // add medium scales
5242 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5243 myScales.reserve( theNbSteps * 2 );
5244 myScales.push_back( 0.5 * ( *s1 + 1. ));
5245 myScales.push_back( *s1 );
5246 for ( ; s2 != theScales.end(); s1 = s2++ )
5248 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5249 myScales.push_back( *s2 );
5253 if ( !theAngles.empty() )
5255 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5256 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5257 linearAngleVariation( theNbSteps, angles );
5259 // accumulate angles
5262 std::list<double>::iterator a1 = angles.begin(), a2;
5263 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5268 while ( nbAngles++ < theNbSteps )
5269 angles.push_back( angles.back() );
5271 // add medium angles
5272 a2 = angles.begin(), a1 = a2++;
5273 myAngles.push_back( 0.5 * *a1 );
5274 myAngles.push_back( *a1 );
5275 for ( ; a2 != angles.end(); a1 = a2++ )
5277 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5278 myAngles.push_back( *a2 );
5284 myBaseP = *theBasePoint;
5287 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5288 ( theTolerance > 0 ))
5290 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose : steps are given explicitly
5301 //=======================================================================
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5304 Handle(TColStd_HSequenceOfReal) theSteps,
5306 const double theTolerance):
5308 mySteps( theSteps ),
5309 myFlags( theFlags ),
5310 myTolerance( theTolerance ),
5311 myElemsToUse( NULL )
5313 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5314 ( theTolerance > 0 ))
5316 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5320 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5324 //=======================================================================
5325 //function : ExtrusParam
5326 //purpose : for extrusion by normal
5327 //=======================================================================
5329 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5330 const int theNbSteps,
5334 mySteps( new TColStd_HSequenceOfReal ),
5335 myFlags( theFlags ),
5337 myElemsToUse( NULL )
5339 for (int i = 0; i < theNbSteps; i++ )
5340 mySteps->Append( theStepSize );
5344 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5348 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5352 //=======================================================================
5353 //function : ExtrusParam
5354 //purpose : for extrusion along path
5355 //=======================================================================
5357 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5358 const gp_Pnt* theBasePoint,
5359 const std::list<double>& theScales,
5360 const bool theMakeGroups )
5361 : myBaseP( Precision::Infinite(), 0, 0 ),
5362 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5363 myPathPoints( thePoints )
5367 myBaseP = theBasePoint->XYZ();
5370 if ( !theScales.empty() )
5372 // add medium scales
5373 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5374 myScales.reserve( thePoints.size() * 2 );
5375 myScales.push_back( 0.5 * ( 1. + *s1 ));
5376 myScales.push_back( *s1 );
5377 for ( ; s2 != theScales.end(); s1 = s2++ )
5379 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5380 myScales.push_back( *s2 );
5384 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5387 //=======================================================================
5388 //function : ExtrusParam::SetElementsToUse
5389 //purpose : stores elements to use for extrusion by normal, depending on
5390 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5391 // define myBaseP for scaling
5392 //=======================================================================
5394 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5395 const TIDSortedElemSet& nodes )
5397 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5399 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5401 myBaseP.SetCoord( 0.,0.,0. );
5402 TIDSortedElemSet newNodes;
5404 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5405 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5407 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5408 TIDSortedElemSet::const_iterator itElem = elements.begin();
5409 for ( ; itElem != elements.end(); itElem++ )
5411 const SMDS_MeshElement* elem = *itElem;
5412 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5413 while ( itN->more() ) {
5414 const SMDS_MeshElement* node = itN->next();
5415 if ( newNodes.insert( node ).second )
5416 myBaseP += SMESH_NodeXYZ( node );
5420 myBaseP /= newNodes.size();
5424 //=======================================================================
5425 //function : ExtrusParam::beginStepIter
5426 //purpose : prepare iteration on steps
5427 //=======================================================================
5429 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5431 myWithMediumNodes = withMediumNodes;
5435 //=======================================================================
5436 //function : ExtrusParam::moreSteps
5437 //purpose : are there more steps?
5438 //=======================================================================
5440 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5442 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5444 //=======================================================================
5445 //function : ExtrusParam::nextStep
5446 //purpose : returns the next step
5447 //=======================================================================
5449 double SMESH_MeshEditor::ExtrusParam::nextStep()
5452 if ( !myCurSteps.empty() )
5454 res = myCurSteps.back();
5455 myCurSteps.pop_back();
5457 else if ( myNextStep <= mySteps->Length() )
5459 myCurSteps.push_back( mySteps->Value( myNextStep ));
5461 if ( myWithMediumNodes )
5463 myCurSteps.back() /= 2.;
5464 myCurSteps.push_back( myCurSteps.back() );
5471 //=======================================================================
5472 //function : ExtrusParam::makeNodesByDir
5473 //purpose : create nodes for standard extrusion
5474 //=======================================================================
5476 int SMESH_MeshEditor::ExtrusParam::
5477 makeNodesByDir( SMESHDS_Mesh* mesh,
5478 const SMDS_MeshNode* srcNode,
5479 std::list<const SMDS_MeshNode*> & newNodes,
5480 const bool makeMediumNodes)
5482 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5485 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5487 p += myDir.XYZ() * nextStep();
5488 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5489 newNodes.push_back( newNode );
5492 if ( !myScales.empty() || !myAngles.empty() )
5494 gp_XYZ center = myBaseP;
5495 gp_Ax1 ratationAxis( center, myDir );
5498 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5499 size_t i = !makeMediumNodes;
5500 for ( beginStepIter( makeMediumNodes );
5502 ++nIt, i += 1 + !makeMediumNodes )
5504 center += myDir.XYZ() * nextStep();
5506 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5508 if ( i < myScales.size() )
5510 xyz = ( myScales[i] * ( xyz - center )) + center;
5513 if ( !myAngles.empty() )
5515 rotation.SetRotation( ratationAxis, myAngles[i] );
5516 rotation.Transforms( xyz );
5520 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5528 //=======================================================================
5529 //function : ExtrusParam::makeNodesByDirAndSew
5530 //purpose : create nodes for standard extrusion with sewing
5531 //=======================================================================
5533 int SMESH_MeshEditor::ExtrusParam::
5534 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5535 const SMDS_MeshNode* srcNode,
5536 std::list<const SMDS_MeshNode*> & newNodes,
5537 const bool makeMediumNodes)
5539 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5542 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5544 P1 += myDir.XYZ() * nextStep();
5546 // try to search in sequence of existing nodes
5547 // if myNodes.size()>0 we 'nave to use given sequence
5548 // else - use all nodes of mesh
5549 const SMDS_MeshNode * node = 0;
5550 if ( myNodes.Length() > 0 )
5552 for ( int i = 1; i <= myNodes.Length(); i++ )
5554 SMESH_NodeXYZ P2 = myNodes.Value(i);
5555 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5557 node = myNodes.Value(i);
5564 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5567 SMESH_NodeXYZ P2 = itn->next();
5568 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5577 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5579 newNodes.push_back( node );
5586 //=======================================================================
5587 //function : ExtrusParam::makeNodesByNormal2D
5588 //purpose : create nodes for extrusion using normals of faces
5589 //=======================================================================
5591 int SMESH_MeshEditor::ExtrusParam::
5592 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5593 const SMDS_MeshNode* srcNode,
5594 std::list<const SMDS_MeshNode*> & newNodes,
5595 const bool makeMediumNodes)
5597 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5599 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5601 // get normals to faces sharing srcNode
5602 vector< gp_XYZ > norms, baryCenters;
5603 gp_XYZ norm, avgNorm( 0,0,0 );
5604 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5605 while ( faceIt->more() )
5607 const SMDS_MeshElement* face = faceIt->next();
5608 if ( myElemsToUse && !myElemsToUse->count( face ))
5610 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5612 norms.push_back( norm );
5614 if ( !alongAvgNorm )
5618 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5619 bc += SMESH_NodeXYZ( nIt->next() );
5620 baryCenters.push_back( bc / nbN );
5625 if ( norms.empty() ) return 0;
5627 double normSize = avgNorm.Modulus();
5628 if ( normSize < std::numeric_limits<double>::min() )
5631 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5634 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5637 avgNorm /= normSize;
5640 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5643 double stepSize = nextStep();
5645 if ( norms.size() > 1 )
5647 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5649 // translate plane of a face
5650 baryCenters[ iF ] += norms[ iF ] * stepSize;
5652 // find point of intersection of the face plane located at baryCenters[ iF ]
5653 // and avgNorm located at pNew
5654 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5655 double dot = ( norms[ iF ] * avgNorm );
5656 if ( dot < std::numeric_limits<double>::min() )
5657 dot = stepSize * 1e-3;
5658 double step = -( norms[ iF ] * pNew + d ) / dot;
5659 pNew += step * avgNorm;
5664 pNew += stepSize * avgNorm;
5668 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5669 newNodes.push_back( newNode );
5674 //=======================================================================
5675 //function : ExtrusParam::makeNodesByNormal1D
5676 //purpose : create nodes for extrusion using normals of edges
5677 //=======================================================================
5679 int SMESH_MeshEditor::ExtrusParam::
5680 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5681 const SMDS_MeshNode* /*srcNode*/,
5682 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5683 const bool /*makeMediumNodes*/)
5685 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5689 //=======================================================================
5690 //function : ExtrusParam::makeNodesAlongTrack
5691 //purpose : create nodes for extrusion along path
5692 //=======================================================================
5694 int SMESH_MeshEditor::ExtrusParam::
5695 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5696 const SMDS_MeshNode* srcNode,
5697 std::list<const SMDS_MeshNode*> & newNodes,
5698 const bool makeMediumNodes)
5700 const Standard_Real aTolAng=1.e-4;
5702 gp_Pnt aV0x = myBaseP;
5703 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5705 const PathPoint& aPP0 = myPathPoints[0];
5706 gp_Pnt aP0x = aPP0.myPnt;
5707 gp_Dir aDT0x= aPP0.myTgt;
5709 std::vector< gp_Pnt > centers;
5710 centers.reserve( NbSteps() * 2 );
5712 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5714 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5716 const PathPoint& aPP = myPathPoints[j];
5717 const gp_Pnt& aP1x = aPP.myPnt;
5718 const gp_Dir& aDT1x = aPP.myTgt;
5721 gp_Vec aV01x( aP0x, aP1x );
5722 aTrsf.SetTranslation( aV01x );
5723 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5724 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5726 // rotation 1 [ T1,T0 ]
5727 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5728 if ( fabs( aAngleT1T0 ) > aTolAng )
5730 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5731 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5733 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5737 if ( aPP.myAngle != 0. )
5739 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5740 aPN1 = aPN1.Transformed( aTrsfRot );
5744 if ( makeMediumNodes )
5746 // create additional node
5747 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5748 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5749 newNodes.push_back( newNode );
5752 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5753 newNodes.push_back( newNode );
5755 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5756 centers.push_back( aV1x );
5765 if ( !myScales.empty() )
5768 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5769 for ( size_t i = !makeMediumNodes;
5770 i < myScales.size() && node != newNodes.end();
5771 i += ( 1 + !makeMediumNodes ), ++node )
5773 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5774 gp_Pnt aN = SMESH_NodeXYZ( *node );
5775 gp_Pnt aP = aN.Transformed( aTrsfScale );
5776 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5780 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5783 //=======================================================================
5784 //function : ExtrusionSweep
5786 //=======================================================================
5788 SMESH_MeshEditor::PGroupIDs
5789 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5790 const gp_Vec& theStep,
5791 const int theNbSteps,
5792 TTElemOfElemListMap& newElemsMap,
5794 const double theTolerance)
5796 std::list<double> dummy;
5797 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5798 theFlags, theTolerance );
5799 return ExtrusionSweep( theElems, aParams, newElemsMap );
5805 //=======================================================================
5806 //function : getOriFactor
5807 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5808 // edge curve orientation
5809 //=======================================================================
5811 double getOriFactor( const TopoDS_Edge& edge,
5812 const SMDS_MeshNode* n1,
5813 const SMDS_MeshNode* n2,
5814 SMESH_MesherHelper& helper)
5816 double u1 = helper.GetNodeU( edge, n1, n2 );
5817 double u2 = helper.GetNodeU( edge, n2, n1 );
5818 return u1 < u2 ? 1. : -1.;
5822 //=======================================================================
5823 //function : ExtrusionSweep
5825 //=======================================================================
5827 SMESH_MeshEditor::PGroupIDs
5828 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5829 ExtrusParam& theParams,
5830 TTElemOfElemListMap& newElemsMap)
5834 setElemsFirst( theElemSets );
5835 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5836 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5838 // source elements for each generated one
5839 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5840 srcElems.reserve( theElemSets[0].size() );
5841 srcNodes.reserve( theElemSets[1].size() );
5843 const int nbSteps = theParams.NbSteps();
5844 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5846 TNodeOfNodeListMap mapNewNodes;
5847 TElemOfVecOfNnlmiMap mapElemNewNodes;
5849 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5850 myMesh->NbFaces(ORDER_QUADRATIC) +
5851 myMesh->NbVolumes(ORDER_QUADRATIC) );
5853 TIDSortedElemSet::iterator itElem;
5854 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5856 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5857 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5859 // check element type
5860 const SMDS_MeshElement* elem = *itElem;
5861 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5864 const size_t nbNodes = elem->NbNodes();
5865 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5866 newNodesItVec.reserve( nbNodes );
5868 // loop on elem nodes
5869 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5870 while ( itN->more() )
5872 // check if a node has been already sweeped
5873 const SMDS_MeshNode* node = itN->next();
5874 TNodeOfNodeListMap::iterator nIt =
5875 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5876 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5877 if ( listNewNodes.empty() )
5881 // check if we are to create medium nodes between corner ones
5882 bool needMediumNodes = false;
5883 if ( isQuadraticMesh )
5885 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5886 while (it->more() && !needMediumNodes )
5888 const SMDS_MeshElement* invElem = it->next();
5889 if ( invElem != elem && !theElems.count( invElem )) continue;
5890 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5891 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5892 needMediumNodes = true;
5895 // create nodes for all steps
5896 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5898 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5899 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5901 myLastCreatedNodes.push_back( *newNodesIt );
5902 srcNodes.push_back( node );
5907 if ( theParams.ToMakeBoundary() )
5909 GetMeshDS()->Modified();
5910 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5912 break; // newNodesItVec will be shorter than nbNodes
5915 newNodesItVec.push_back( nIt );
5917 // make new elements
5918 if ( newNodesItVec.size() == nbNodes )
5919 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5923 if ( theParams.ToMakeBoundary() ) {
5924 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5926 PGroupIDs newGroupIDs;
5927 if ( theParams.ToMakeGroups() )
5928 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5933 //=======================================================================
5934 //function : ExtrusionAlongTrack
5936 //=======================================================================
5937 SMESH_MeshEditor::Extrusion_Error
5938 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5939 SMESH_Mesh* theTrackMesh,
5940 SMDS_ElemIteratorPtr theTrackIterator,
5941 const SMDS_MeshNode* theN1,
5942 std::list<double>& theAngles,
5943 const bool theAngleVariation,
5944 std::list<double>& theScales,
5945 const bool theScaleVariation,
5946 const gp_Pnt* theRefPoint,
5947 const bool theMakeGroups)
5952 if ( theElements[0].empty() && theElements[1].empty() )
5953 return EXTR_NO_ELEMENTS;
5955 ASSERT( theTrackMesh );
5956 if ( ! theTrackIterator || !theTrackIterator->more() )
5957 return EXTR_NO_ELEMENTS;
5959 // 2. Get ordered nodes
5960 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5961 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5962 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5963 if ( branchEdges.empty() )
5964 return EXTR_PATH_NOT_EDGE;
5966 if ( branchEdges.size() > 1 )
5967 return EXTR_BAD_PATH_SHAPE;
5969 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5970 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5971 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5972 return EXTR_BAD_STARTING_NODE;
5974 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5976 // add medium nodes to pathNodes
5977 std::vector< const SMDS_MeshNode* > pathNodes2;
5978 std::vector< const SMDS_MeshElement* > pathEdges2;
5979 pathNodes2.reserve( pathNodes.size() * 2 );
5980 pathEdges2.reserve( pathEdges.size() * 2 );
5981 for ( size_t i = 0; i < pathEdges.size(); ++i )
5983 pathNodes2.push_back( pathNodes[i] );
5984 pathEdges2.push_back( pathEdges[i] );
5985 if ( pathEdges[i]->IsQuadratic() )
5987 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5988 pathEdges2.push_back( pathEdges[i] );
5991 pathNodes2.push_back( pathNodes.back() );
5992 pathEdges.swap( pathEdges2 );
5993 pathNodes.swap( pathNodes2 );
5996 // 3. Get path data at pathNodes
5998 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6000 if ( theAngleVariation )
6001 linearAngleVariation( points.size()-1, theAngles );
6002 if ( theScaleVariation )
6003 linearScaleVariation( points.size()-1, theScales );
6005 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6006 std::list<double>::iterator angle = theAngles.begin();
6008 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6010 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6011 std::map< int, double >::iterator id2factor;
6012 SMESH_MesherHelper pathHelper( *theTrackMesh );
6013 gp_Pnt p; gp_Vec tangent;
6014 const double tol2 = gp::Resolution() * gp::Resolution();
6016 for ( size_t i = 0; i < pathNodes.size(); ++i )
6018 ExtrusParam::PathPoint & point = points[ i ];
6020 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6022 if ( angle != theAngles.end() )
6023 point.myAngle = *angle++;
6025 tangent.SetCoord( 0,0,0 );
6026 const int shapeID = pathNodes[ i ]->GetShapeID();
6027 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6028 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6029 switch ( shapeType )
6033 TopoDS_Edge edge = TopoDS::Edge( shape );
6034 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6035 if ( id2factor->second == 0 )
6037 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6038 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6040 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6041 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6042 curve->D1( u, p, tangent );
6043 tangent *= id2factor->second;
6049 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6050 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6052 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6053 for ( int di = -1; di <= 0; ++di )
6056 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6058 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6059 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6060 if ( id2factor->second == 0 )
6063 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6065 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6067 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6068 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6070 curve->D1( u, p, du );
6071 double size2 = du.SquareMagnitude();
6072 if ( du.SquareMagnitude() > tol2 )
6074 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6087 for ( int di = -1; di <= 1; di += 2 )
6090 if ( j < pathNodes.size() )
6092 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6093 double size2 = dir.SquareMagnitude();
6095 tangent += dir.Divided( Sqrt( size2 )) * di;
6099 } // switch ( shapeType )
6101 if ( tangent.SquareMagnitude() < tol2 )
6102 return EXTR_CANT_GET_TANGENT;
6104 point.myTgt = tangent;
6106 } // loop on pathNodes
6109 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6110 TTElemOfElemListMap newElemsMap;
6112 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6117 //=======================================================================
6118 //function : linearAngleVariation
6119 //purpose : spread values over nbSteps
6120 //=======================================================================
6122 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6123 list<double>& Angles)
6125 int nbAngles = Angles.size();
6126 if( nbSteps > nbAngles && nbAngles > 0 )
6128 vector<double> theAngles(nbAngles);
6129 theAngles.assign( Angles.begin(), Angles.end() );
6132 double rAn2St = double( nbAngles ) / double( nbSteps );
6133 double angPrev = 0, angle;
6134 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6136 double angCur = rAn2St * ( iSt+1 );
6137 double angCurFloor = floor( angCur );
6138 double angPrevFloor = floor( angPrev );
6139 if ( angPrevFloor == angCurFloor )
6140 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6142 int iP = int( angPrevFloor );
6143 double angPrevCeil = ceil(angPrev);
6144 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6146 int iC = int( angCurFloor );
6147 if ( iC < nbAngles )
6148 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6150 iP = int( angPrevCeil );
6152 angle += theAngles[ iC ];
6154 res.push_back(angle);
6161 //=======================================================================
6162 //function : linearScaleVariation
6163 //purpose : spread values over nbSteps
6164 //=======================================================================
6166 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6167 std::list<double>& theScales)
6169 int nbScales = theScales.size();
6170 std::vector<double> myScales;
6171 myScales.reserve( theNbSteps );
6172 std::list<double>::const_iterator scale = theScales.begin();
6173 double prevScale = 1.0;
6174 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6176 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6177 int stDelta = Max( 1, iStep - myScales.size());
6178 double scDelta = ( *scale - prevScale ) / stDelta;
6179 for ( int iStep = 0; iStep < stDelta; ++iStep )
6181 myScales.push_back( prevScale + scDelta );
6182 prevScale = myScales.back();
6186 theScales.assign( myScales.begin(), myScales.end() );
6189 //================================================================================
6191 * \brief Move or copy theElements applying theTrsf to their nodes
6192 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6193 * \param theTrsf - transformation to apply
6194 * \param theCopy - if true, create translated copies of theElems
6195 * \param theMakeGroups - if true and theCopy, create translated groups
6196 * \param theTargetMesh - mesh to copy translated elements into
6197 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6199 //================================================================================
6201 SMESH_MeshEditor::PGroupIDs
6202 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6203 const gp_Trsf& theTrsf,
6205 const bool theMakeGroups,
6206 SMESH_Mesh* theTargetMesh)
6209 myLastCreatedElems.reserve( theElems.size() );
6211 bool needReverse = false;
6212 string groupPostfix;
6213 switch ( theTrsf.Form() ) {
6216 groupPostfix = "mirrored";
6219 groupPostfix = "mirrored";
6223 groupPostfix = "mirrored";
6226 groupPostfix = "rotated";
6228 case gp_Translation:
6229 groupPostfix = "translated";
6232 groupPostfix = "scaled";
6234 case gp_CompoundTrsf: // different scale by axis
6235 groupPostfix = "scaled";
6238 needReverse = false;
6239 groupPostfix = "transformed";
6242 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6243 SMESHDS_Mesh* aMesh = GetMeshDS();
6245 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6246 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6247 SMESH_MeshEditor::ElemFeatures elemType;
6249 // map old node to new one
6250 TNodeNodeMap nodeMap;
6252 // elements sharing moved nodes; those of them which have all
6253 // nodes mirrored but are not in theElems are to be reversed
6254 TIDSortedElemSet inverseElemSet;
6256 // source elements for each generated one
6257 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6259 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6260 TIDSortedElemSet orphanNode;
6262 if ( theElems.empty() ) // transform the whole mesh
6265 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6266 while ( eIt->more() ) theElems.insert( eIt->next() );
6268 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6269 while ( nIt->more() )
6271 const SMDS_MeshNode* node = nIt->next();
6272 if ( node->NbInverseElements() == 0)
6273 orphanNode.insert( node );
6277 // loop on elements to transform nodes : first orphan nodes then elems
6278 TIDSortedElemSet::iterator itElem;
6279 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6280 for (int i=0; i<2; i++)
6281 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6283 const SMDS_MeshElement* elem = *itElem;
6287 // loop on elem nodes
6289 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6290 while ( itN->more() )
6292 const SMDS_MeshNode* node = cast2Node( itN->next() );
6293 // check if a node has been already transformed
6294 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6295 nodeMap.insert( make_pair ( node, node ));
6296 if ( !n2n_isnew.second )
6299 node->GetXYZ( coord );
6300 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6301 if ( theTargetMesh ) {
6302 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6303 n2n_isnew.first->second = newNode;
6304 myLastCreatedNodes.push_back(newNode);
6305 srcNodes.push_back( node );
6307 else if ( theCopy ) {
6308 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6309 n2n_isnew.first->second = newNode;
6310 myLastCreatedNodes.push_back(newNode);
6311 srcNodes.push_back( node );
6314 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6315 // node position on shape becomes invalid
6316 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6317 ( SMDS_SpacePosition::originSpacePosition() );
6320 // keep inverse elements
6321 if ( !theCopy && !theTargetMesh && needReverse ) {
6322 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6323 while ( invElemIt->more() ) {
6324 const SMDS_MeshElement* iel = invElemIt->next();
6325 inverseElemSet.insert( iel );
6329 } // loop on elems in { &orphanNode, &theElems };
6331 // either create new elements or reverse mirrored ones
6332 if ( !theCopy && !needReverse && !theTargetMesh )
6335 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6337 // Replicate or reverse elements
6339 std::vector<int> iForw;
6340 vector<const SMDS_MeshNode*> nodes;
6341 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6343 const SMDS_MeshElement* elem = *itElem;
6344 if ( !elem ) continue;
6346 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6347 size_t nbNodes = elem->NbNodes();
6348 if ( geomType == SMDSGeom_NONE ) continue; // node
6350 nodes.resize( nbNodes );
6352 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6354 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6358 bool allTransformed = true;
6359 int nbFaces = aPolyedre->NbFaces();
6360 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6362 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6363 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6365 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6366 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6367 if ( nodeMapIt == nodeMap.end() )
6368 allTransformed = false; // not all nodes transformed
6370 nodes.push_back((*nodeMapIt).second);
6372 if ( needReverse && allTransformed )
6373 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6375 if ( !allTransformed )
6376 continue; // not all nodes transformed
6378 else // ----------------------- the rest element types
6380 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6381 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6382 const vector<int>& i = needReverse ? iRev : iForw;
6384 // find transformed nodes
6386 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6387 while ( itN->more() ) {
6388 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6389 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6390 if ( nodeMapIt == nodeMap.end() )
6391 break; // not all nodes transformed
6392 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6394 if ( iNode != nbNodes )
6395 continue; // not all nodes transformed
6399 // copy in this or a new mesh
6400 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6401 srcElems.push_back( elem );
6404 // reverse element as it was reversed by transformation
6406 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6409 } // loop on elements
6411 if ( editor && editor != this )
6412 myLastCreatedElems.swap( editor->myLastCreatedElems );
6414 PGroupIDs newGroupIDs;
6416 if ( ( theMakeGroups && theCopy ) ||
6417 ( theMakeGroups && theTargetMesh ) )
6418 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6423 //================================================================================
6425 * \brief Make an offset mesh from a source 2D mesh
6426 * \param [in] theElements - source faces
6427 * \param [in] theValue - offset value
6428 * \param [out] theTgtMesh - a mesh to add offset elements to
6429 * \param [in] theMakeGroups - to generate groups
6430 * \return PGroupIDs - IDs of created groups. NULL means failure
6432 //================================================================================
6434 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6435 const double theValue,
6436 SMESH_Mesh* theTgtMesh,
6437 const bool theMakeGroups,
6438 const bool theCopyElements,
6439 const bool theFixSelfIntersection)
6441 SMESHDS_Mesh* meshDS = GetMeshDS();
6442 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6443 SMESH_MeshEditor tgtEditor( theTgtMesh );
6445 SMDS_ElemIteratorPtr eIt;
6446 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6447 else eIt = SMESHUtils::elemSetIterator( theElements );
6449 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6450 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6451 std::unique_ptr< SMDS_Mesh > offsetMesh
6452 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6453 theFixSelfIntersection,
6454 new2OldFaces, new2OldNodes ));
6455 if ( offsetMesh->NbElements() == 0 )
6456 return PGroupIDs(); // MakeOffset() failed
6459 if ( theTgtMesh == myMesh && !theCopyElements )
6461 // clear the source elements
6462 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6463 else eIt = SMESHUtils::elemSetIterator( theElements );
6464 while ( eIt->more() )
6465 meshDS->RemoveFreeElement( eIt->next(), 0 );
6468 // offsetMesh->Modified();
6469 // offsetMesh->CompactMesh(); // make IDs start from 1
6471 // source elements for each generated one
6472 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6473 srcElems.reserve( new2OldFaces.size() );
6474 srcNodes.reserve( new2OldNodes.size() );
6477 myLastCreatedElems.reserve( new2OldFaces.size() );
6478 myLastCreatedNodes.reserve( new2OldNodes.size() );
6480 // copy offsetMesh to theTgtMesh
6482 int idShift = meshDS->MaxNodeID();
6483 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6484 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6487 if ( n->NbInverseElements() > 0 )
6490 const SMDS_MeshNode* n2 =
6491 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6492 myLastCreatedNodes.push_back( n2 );
6493 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6497 ElemFeatures elemType;
6498 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6499 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6502 elemType.myNodes.clear();
6503 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6505 const SMDS_MeshNode* n2 = nIt->next();
6506 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6508 tgtEditor.AddElement( elemType.myNodes, elemType );
6509 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6512 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6514 PGroupIDs newGroupIDs;
6515 if ( theMakeGroups )
6516 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6518 newGroupIDs.reset( new std::list< int > );
6523 //=======================================================================
6525 * \brief Create groups of elements made during transformation
6526 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6527 * \param elemGens - elements making corresponding myLastCreatedElems
6528 * \param postfix - to push_back to names of new groups
6529 * \param targetMesh - mesh to create groups in
6530 * \param topPresent - is there are "top" elements that are created by sweeping
6532 //=======================================================================
6534 SMESH_MeshEditor::PGroupIDs
6535 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6536 const SMESH_SequenceOfElemPtr& elemGens,
6537 const std::string& postfix,
6538 SMESH_Mesh* targetMesh,
6539 const bool topPresent)
6541 PGroupIDs newGroupIDs( new list<int> );
6542 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6544 // Sort existing groups by types and collect their names
6546 // containers to store an old group and generated new ones;
6547 // 1st new group is for result elems of different type than a source one;
6548 // 2nd new group is for same type result elems ("top" group at extrusion)
6550 using boost::make_tuple;
6551 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6552 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6553 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6555 set< string > groupNames;
6557 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6558 if ( !groupIt->more() ) return newGroupIDs;
6560 int newGroupID = mesh->GetGroupIds().back()+1;
6561 while ( groupIt->more() )
6563 SMESH_Group * group = groupIt->next();
6564 if ( !group ) continue;
6565 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6566 if ( !groupDS || groupDS->IsEmpty() ) continue;
6567 groupNames.insert ( group->GetName() );
6568 groupDS->SetStoreName( group->GetName() );
6569 const SMDSAbs_ElementType type = groupDS->GetType();
6570 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6571 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6572 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6573 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6576 // Loop on nodes and elements to add them in new groups
6578 vector< const SMDS_MeshElement* > resultElems;
6579 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6581 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6582 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6583 if ( gens.size() != elems.size() )
6584 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6586 // loop on created elements
6587 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6589 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6590 if ( !sourceElem ) {
6591 MESSAGE("generateGroups(): NULL source element");
6594 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6595 if ( groupsOldNew.empty() ) { // no groups of this type at all
6596 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6597 ++iElem; // skip all elements made by sourceElem
6600 // collect all elements made by the iElem-th sourceElem
6601 resultElems.clear();
6602 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6603 if ( resElem != sourceElem )
6604 resultElems.push_back( resElem );
6605 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6606 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6607 if ( resElem != sourceElem )
6608 resultElems.push_back( resElem );
6610 const SMDS_MeshElement* topElem = 0;
6611 if ( isNodes ) // there must be a top element
6613 topElem = resultElems.back();
6614 resultElems.pop_back();
6618 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6619 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6620 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6622 topElem = *resElemIt;
6623 *resElemIt = 0; // erase *resElemIt
6627 // add resultElems to groups originted from ones the sourceElem belongs to
6628 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6629 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6631 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6632 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6634 // fill in a new group
6635 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6636 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6637 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6639 newGroup.Add( *resElemIt );
6641 // fill a "top" group
6644 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6645 newTopGroup.Add( topElem );
6649 } // loop on created elements
6650 }// loop on nodes and elements
6652 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6654 list<int> topGrouIds;
6655 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6657 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6658 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6659 orderedOldNewGroups[i]->get<2>() };
6660 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6662 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6663 if ( newGroupDS->IsEmpty() )
6665 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6670 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6673 const bool isTop = ( topPresent &&
6674 newGroupDS->GetType() == oldGroupDS->GetType() &&
6677 string name = oldGroupDS->GetStoreName();
6678 { // remove trailing whitespaces (issue 22599)
6679 size_t size = name.size();
6680 while ( size > 1 && isspace( name[ size-1 ]))
6682 if ( size != name.size() )
6684 name.resize( size );
6685 oldGroupDS->SetStoreName( name.c_str() );
6688 if ( !targetMesh ) {
6689 string suffix = ( isTop ? "top": postfix.c_str() );
6693 while ( !groupNames.insert( name ).second ) // name exists
6694 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6699 newGroupDS->SetStoreName( name.c_str() );
6701 // make a SMESH_Groups
6702 mesh->AddGroup( newGroupDS );
6704 topGrouIds.push_back( newGroupDS->GetID() );
6706 newGroupIDs->push_back( newGroupDS->GetID() );
6710 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6715 //================================================================================
6717 * * \brief Return list of group of nodes close to each other within theTolerance
6718 * * Search among theNodes or in the whole mesh if theNodes is empty using
6719 * * an Octree algorithm
6720 * \param [in,out] theNodes - the nodes to treat
6721 * \param [in] theTolerance - the tolerance
6722 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6723 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6724 * corner and medium nodes in separate groups
6726 //================================================================================
6728 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6729 const double theTolerance,
6730 TListOfListOfNodes & theGroupsOfNodes,
6731 bool theSeparateCornersAndMedium)
6735 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6736 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6737 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6738 theSeparateCornersAndMedium = false;
6740 TIDSortedNodeSet& corners = theNodes;
6741 TIDSortedNodeSet medium;
6743 if ( theNodes.empty() ) // get all nodes in the mesh
6745 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6746 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6747 if ( theSeparateCornersAndMedium )
6748 while ( nIt->more() )
6750 const SMDS_MeshNode* n = nIt->next();
6751 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6752 nodeSet->insert( nodeSet->end(), n );
6755 while ( nIt->more() )
6756 theNodes.insert( theNodes.end(), nIt->next() );
6758 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6760 TIDSortedNodeSet::iterator nIt = corners.begin();
6761 while ( nIt != corners.end() )
6762 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6764 medium.insert( medium.end(), *nIt );
6765 corners.erase( nIt++ );
6773 if ( !corners.empty() )
6774 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6775 if ( !medium.empty() )
6776 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6779 //=======================================================================
6780 //function : SimplifyFace
6781 //purpose : split a chain of nodes into several closed chains
6782 //=======================================================================
6784 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6785 vector<const SMDS_MeshNode *>& poly_nodes,
6786 vector<int>& quantities) const
6788 int nbNodes = faceNodes.size();
6789 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6793 size_t prevNbQuant = quantities.size();
6795 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6796 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6797 map< const SMDS_MeshNode*, int >::iterator nInd;
6799 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6800 simpleNodes.push_back( faceNodes[0] );
6801 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6803 if ( faceNodes[ iCur ] != simpleNodes.back() )
6805 int index = simpleNodes.size();
6806 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6807 int prevIndex = nInd->second;
6808 if ( prevIndex < index )
6811 int loopLen = index - prevIndex;
6814 // store the sub-loop
6815 quantities.push_back( loopLen );
6816 for ( int i = prevIndex; i < index; i++ )
6817 poly_nodes.push_back( simpleNodes[ i ]);
6819 simpleNodes.resize( prevIndex+1 );
6823 simpleNodes.push_back( faceNodes[ iCur ]);
6828 if ( simpleNodes.size() > 2 )
6830 quantities.push_back( simpleNodes.size() );
6831 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6834 return quantities.size() - prevNbQuant;
6837 //=======================================================================
6838 //function : MergeNodes
6839 //purpose : In each group, the cdr of nodes are substituted by the first one
6841 //=======================================================================
6843 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6844 const bool theAvoidMakingHoles)
6848 SMESHDS_Mesh* mesh = GetMeshDS();
6850 TNodeNodeMap nodeNodeMap; // node to replace - new node
6851 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6852 list< int > rmElemIds, rmNodeIds;
6853 vector< ElemFeatures > newElemDefs;
6855 // Fill nodeNodeMap and elems
6857 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6858 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6860 list<const SMDS_MeshNode*>& nodes = *grIt;
6861 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6862 const SMDS_MeshNode* nToKeep = *nIt;
6863 for ( ++nIt; nIt != nodes.end(); nIt++ )
6865 const SMDS_MeshNode* nToRemove = *nIt;
6866 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6867 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6868 while ( invElemIt->more() ) {
6869 const SMDS_MeshElement* elem = invElemIt->next();
6875 // Apply recursive replacements (BUG 0020185)
6876 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6877 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6879 const SMDS_MeshNode* nToKeep = nnIt->second;
6880 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6881 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6883 nToKeep = nnIt_i->second;
6884 nnIt->second = nToKeep;
6885 nnIt_i = nodeNodeMap.find( nToKeep );
6889 if ( theAvoidMakingHoles )
6891 // find elements whose topology changes
6893 vector<const SMDS_MeshElement*> pbElems;
6894 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6895 for ( ; eIt != elems.end(); ++eIt )
6897 const SMDS_MeshElement* elem = *eIt;
6898 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6899 while ( itN->more() )
6901 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6902 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6903 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6905 // several nodes of elem stick
6906 pbElems.push_back( elem );
6911 // exclude from merge nodes causing spoiling element
6912 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6914 bool nodesExcluded = false;
6915 for ( size_t i = 0; i < pbElems.size(); ++i )
6917 size_t prevNbMergeNodes = nodeNodeMap.size();
6918 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6919 prevNbMergeNodes < nodeNodeMap.size() )
6920 nodesExcluded = true;
6922 if ( !nodesExcluded )
6927 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6929 const SMDS_MeshNode* nToRemove = nnIt->first;
6930 const SMDS_MeshNode* nToKeep = nnIt->second;
6931 if ( nToRemove != nToKeep )
6933 rmNodeIds.push_back( nToRemove->GetID() );
6934 AddToSameGroups( nToKeep, nToRemove, mesh );
6935 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6936 // w/o creating node in place of merged ones.
6937 SMDS_PositionPtr pos = nToRemove->GetPosition();
6938 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6939 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6940 sm->SetIsAlwaysComputed( true );
6944 // Change element nodes or remove an element
6946 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6947 for ( ; eIt != elems.end(); eIt++ )
6949 const SMDS_MeshElement* elem = *eIt;
6950 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6952 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6954 rmElemIds.push_back( elem->GetID() );
6956 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6958 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6959 & newElemDefs[i].myNodes[0],
6960 newElemDefs[i].myNodes.size() ))
6964 newElemDefs[i].SetID( elem->GetID() );
6965 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6966 if ( !keepElem ) rmElemIds.pop_back();
6970 newElemDefs[i].SetID( -1 );
6972 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6973 if ( sm && newElem )
6974 sm->AddElement( newElem );
6975 if ( elem != newElem )
6976 ReplaceElemInGroups( elem, newElem, mesh );
6981 // Remove bad elements, then equal nodes (order important)
6982 Remove( rmElemIds, /*isNodes=*/false );
6983 Remove( rmNodeIds, /*isNodes=*/true );
6988 //=======================================================================
6989 //function : applyMerge
6990 //purpose : Compute new connectivity of an element after merging nodes
6991 // \param [in] elems - the element
6992 // \param [out] newElemDefs - definition(s) of result element(s)
6993 // \param [inout] nodeNodeMap - nodes to merge
6994 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6995 // after merging (but not degenerated), removes nodes causing
6996 // the invalidity from \a nodeNodeMap.
6997 // \return bool - true if the element should be removed
6998 //=======================================================================
7000 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7001 vector< ElemFeatures >& newElemDefs,
7002 TNodeNodeMap& nodeNodeMap,
7003 const bool avoidMakingHoles )
7005 bool toRemove = false; // to remove elem
7006 int nbResElems = 1; // nb new elements
7008 newElemDefs.resize(nbResElems);
7009 newElemDefs[0].Init( elem );
7010 newElemDefs[0].myNodes.clear();
7012 set<const SMDS_MeshNode*> nodeSet;
7013 vector< const SMDS_MeshNode*> curNodes;
7014 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7017 const int nbNodes = elem->NbNodes();
7018 SMDSAbs_EntityType entity = elem->GetEntityType();
7020 curNodes.resize( nbNodes );
7021 uniqueNodes.resize( nbNodes );
7022 iRepl.resize( nbNodes );
7023 int iUnique = 0, iCur = 0, nbRepl = 0;
7025 // Get new seq of nodes
7027 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7028 while ( itN->more() )
7030 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7032 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7033 if ( nnIt != nodeNodeMap.end() ) {
7036 curNodes[ iCur ] = n;
7037 bool isUnique = nodeSet.insert( n ).second;
7039 uniqueNodes[ iUnique++ ] = n;
7041 iRepl[ nbRepl++ ] = iCur;
7045 // Analyse element topology after replacement
7047 int nbUniqueNodes = nodeSet.size();
7048 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7053 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7055 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7056 int nbCorners = nbNodes / 2;
7057 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7059 int iNext = ( iCur + 1 ) % nbCorners;
7060 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7062 int iMedium = iCur + nbCorners;
7063 vector< const SMDS_MeshNode* >::iterator i =
7064 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7066 curNodes[ iMedium ]);
7067 if ( i != uniqueNodes.end() )
7070 for ( ; i+1 != uniqueNodes.end(); ++i )
7079 case SMDSEntity_Polygon:
7080 case SMDSEntity_Quad_Polygon: // Polygon
7082 ElemFeatures* elemType = & newElemDefs[0];
7083 const bool isQuad = elemType->myIsQuad;
7085 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7086 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7088 // a polygon can divide into several elements
7089 vector<const SMDS_MeshNode *> polygons_nodes;
7090 vector<int> quantities;
7091 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7092 newElemDefs.resize( nbResElems );
7093 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7095 ElemFeatures* elemType = & newElemDefs[iface];
7096 if ( iface ) elemType->Init( elem );
7098 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7099 int nbNewNodes = quantities[iface];
7100 face_nodes.assign( polygons_nodes.begin() + inode,
7101 polygons_nodes.begin() + inode + nbNewNodes );
7102 inode += nbNewNodes;
7103 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7105 bool isValid = ( nbNewNodes % 2 == 0 );
7106 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7107 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7108 elemType->SetQuad( isValid );
7109 if ( isValid ) // put medium nodes after corners
7110 SMDS_MeshCell::applyInterlaceRev
7111 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7112 nbNewNodes ), face_nodes );
7114 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7116 nbUniqueNodes = newElemDefs[0].myNodes.size();
7120 case SMDSEntity_Polyhedra: // Polyhedral volume
7122 if ( nbUniqueNodes >= 4 )
7124 // each face has to be analyzed in order to check volume validity
7125 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7127 int nbFaces = aPolyedre->NbFaces();
7129 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7130 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7131 vector<const SMDS_MeshNode *> faceNodes;
7135 for (int iface = 1; iface <= nbFaces; iface++)
7137 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7138 faceNodes.resize( nbFaceNodes );
7139 for (int inode = 1; inode <= nbFaceNodes; inode++)
7141 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7142 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7143 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7144 faceNode = (*nnIt).second;
7145 faceNodes[inode - 1] = faceNode;
7147 SimplifyFace(faceNodes, poly_nodes, quantities);
7150 if ( quantities.size() > 3 )
7152 // TODO: remove coincident faces
7154 nbUniqueNodes = newElemDefs[0].myNodes.size();
7162 // TODO not all the possible cases are solved. Find something more generic?
7163 case SMDSEntity_Edge: //////// EDGE
7164 case SMDSEntity_Triangle: //// TRIANGLE
7165 case SMDSEntity_Quad_Triangle:
7166 case SMDSEntity_Tetra:
7167 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7171 case SMDSEntity_Quad_Edge:
7175 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7177 if ( nbUniqueNodes < 3 )
7179 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7180 toRemove = true; // opposite nodes stick
7185 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7194 if ( nbUniqueNodes == 6 &&
7196 ( nbRepl == 1 || iRepl[1] >= 4 ))
7202 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7211 if ( nbUniqueNodes == 7 &&
7213 ( nbRepl == 1 || iRepl[1] != 8 ))
7219 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7221 if ( nbUniqueNodes == 4 ) {
7222 // ---------------------------------> tetrahedron
7223 if ( curNodes[3] == curNodes[4] &&
7224 curNodes[3] == curNodes[5] ) {
7228 else if ( curNodes[0] == curNodes[1] &&
7229 curNodes[0] == curNodes[2] ) {
7230 // bottom nodes stick: set a top before
7231 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7232 uniqueNodes[ 0 ] = curNodes [ 5 ];
7233 uniqueNodes[ 1 ] = curNodes [ 4 ];
7234 uniqueNodes[ 2 ] = curNodes [ 3 ];
7237 else if (( curNodes[0] == curNodes[3] ) +
7238 ( curNodes[1] == curNodes[4] ) +
7239 ( curNodes[2] == curNodes[5] ) == 2 ) {
7240 // a lateral face turns into a line
7244 else if ( nbUniqueNodes == 5 ) {
7245 // PENTAHEDRON --------------------> pyramid
7246 if ( curNodes[0] == curNodes[3] )
7248 uniqueNodes[ 0 ] = curNodes[ 1 ];
7249 uniqueNodes[ 1 ] = curNodes[ 4 ];
7250 uniqueNodes[ 2 ] = curNodes[ 5 ];
7251 uniqueNodes[ 3 ] = curNodes[ 2 ];
7252 uniqueNodes[ 4 ] = curNodes[ 0 ];
7255 if ( curNodes[1] == curNodes[4] )
7257 uniqueNodes[ 0 ] = curNodes[ 0 ];
7258 uniqueNodes[ 1 ] = curNodes[ 2 ];
7259 uniqueNodes[ 2 ] = curNodes[ 5 ];
7260 uniqueNodes[ 3 ] = curNodes[ 3 ];
7261 uniqueNodes[ 4 ] = curNodes[ 1 ];
7264 if ( curNodes[2] == curNodes[5] )
7266 uniqueNodes[ 0 ] = curNodes[ 0 ];
7267 uniqueNodes[ 1 ] = curNodes[ 3 ];
7268 uniqueNodes[ 2 ] = curNodes[ 4 ];
7269 uniqueNodes[ 3 ] = curNodes[ 1 ];
7270 uniqueNodes[ 4 ] = curNodes[ 2 ];
7276 case SMDSEntity_Hexa:
7278 //////////////////////////////////// HEXAHEDRON
7279 SMDS_VolumeTool hexa (elem);
7280 hexa.SetExternalNormal();
7281 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7282 //////////////////////// HEX ---> tetrahedron
7283 for ( int iFace = 0; iFace < 6; iFace++ ) {
7284 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7285 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7286 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7287 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7288 // one face turns into a point ...
7289 int pickInd = ind[ 0 ];
7290 int iOppFace = hexa.GetOppFaceIndex( iFace );
7291 ind = hexa.GetFaceNodesIndices( iOppFace );
7293 uniqueNodes.clear();
7294 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7295 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7298 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7300 if ( nbStick == 1 ) {
7301 // ... and the opposite one - into a triangle.
7303 uniqueNodes.push_back( curNodes[ pickInd ]);
7310 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7311 //////////////////////// HEX ---> prism
7312 int nbTria = 0, iTria[3];
7313 const int *ind; // indices of face nodes
7314 // look for triangular faces
7315 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7316 ind = hexa.GetFaceNodesIndices( iFace );
7317 TIDSortedNodeSet faceNodes;
7318 for ( iCur = 0; iCur < 4; iCur++ )
7319 faceNodes.insert( curNodes[ind[iCur]] );
7320 if ( faceNodes.size() == 3 )
7321 iTria[ nbTria++ ] = iFace;
7323 // check if triangles are opposite
7324 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7326 // set nodes of the bottom triangle
7327 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7329 for ( iCur = 0; iCur < 4; iCur++ )
7330 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7331 indB.push_back( ind[iCur] );
7332 if ( !hexa.IsForward() )
7333 std::swap( indB[0], indB[2] );
7334 for ( iCur = 0; iCur < 3; iCur++ )
7335 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7336 // set nodes of the top triangle
7337 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7338 for ( iCur = 0; iCur < 3; ++iCur )
7339 for ( int j = 0; j < 4; ++j )
7340 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7342 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7349 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7350 //////////////////// HEXAHEDRON ---> pyramid
7351 for ( int iFace = 0; iFace < 6; iFace++ ) {
7352 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7353 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7354 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7355 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7356 // one face turns into a point ...
7357 int iOppFace = hexa.GetOppFaceIndex( iFace );
7358 ind = hexa.GetFaceNodesIndices( iOppFace );
7359 uniqueNodes.clear();
7360 for ( iCur = 0; iCur < 4; iCur++ ) {
7361 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7364 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7366 if ( uniqueNodes.size() == 4 ) {
7367 // ... and the opposite one is a quadrangle
7369 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7370 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7378 if ( toRemove && nbUniqueNodes > 4 ) {
7379 ////////////////// HEXAHEDRON ---> polyhedron
7380 hexa.SetExternalNormal();
7381 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7382 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7383 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7384 quantities.reserve( 6 ); quantities.clear();
7385 for ( int iFace = 0; iFace < 6; iFace++ )
7387 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7388 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7389 curNodes[ind[1]] == curNodes[ind[3]] )
7392 break; // opposite nodes stick
7395 for ( iCur = 0; iCur < 4; iCur++ )
7397 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7398 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7400 if ( nodeSet.size() < 3 )
7401 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7403 quantities.push_back( nodeSet.size() );
7405 if ( quantities.size() >= 4 )
7408 nbUniqueNodes = poly_nodes.size();
7409 newElemDefs[0].SetPoly(true);
7413 } // case HEXAHEDRON
7418 } // switch ( entity )
7420 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7422 // erase from nodeNodeMap nodes whose merge spoils elem
7423 vector< const SMDS_MeshNode* > noMergeNodes;
7424 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7425 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7426 nodeNodeMap.erase( noMergeNodes[i] );
7429 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7431 uniqueNodes.resize( nbUniqueNodes );
7433 if ( !toRemove && nbResElems == 0 )
7436 newElemDefs.resize( nbResElems );
7442 // ========================================================
7443 // class : ComparableElement
7444 // purpose : allow comparing elements basing on their nodes
7445 // ========================================================
7447 class ComparableElement : public boost::container::flat_set< int >
7449 typedef boost::container::flat_set< int > int_set;
7451 const SMDS_MeshElement* myElem;
7453 mutable int myGroupID;
7457 ComparableElement( const SMDS_MeshElement* theElem ):
7458 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7460 this->reserve( theElem->NbNodes() );
7461 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7463 int id = nodeIt->next()->GetID();
7469 const SMDS_MeshElement* GetElem() const { return myElem; }
7471 int& GroupID() const { return myGroupID; }
7472 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7474 ComparableElement( const ComparableElement& theSource ) // move copy
7477 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7478 (int_set&) (*this ) = std::move( src );
7479 myElem = src.myElem;
7480 mySumID = src.mySumID;
7481 myGroupID = src.myGroupID;
7484 static int HashCode(const ComparableElement& se, int limit )
7486 return ::HashCode( se.mySumID, limit );
7488 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7490 return ( se1 == se2 );
7495 //=======================================================================
7496 //function : FindEqualElements
7497 //purpose : Return list of group of elements built on the same nodes.
7498 // Search among theElements or in the whole mesh if theElements is empty
7499 //=======================================================================
7501 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7502 TListOfListOfElementsID & theGroupsOfElementsID )
7506 SMDS_ElemIteratorPtr elemIt;
7507 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7508 else elemIt = SMESHUtils::elemSetIterator( theElements );
7510 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7511 typedef std::list<int> TGroupOfElems;
7512 TMapOfElements mapOfElements;
7513 std::vector< TGroupOfElems > arrayOfGroups;
7514 TGroupOfElems groupOfElems;
7516 while ( elemIt->more() )
7518 const SMDS_MeshElement* curElem = elemIt->next();
7519 if ( curElem->IsNull() )
7521 ComparableElement compElem = curElem;
7523 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7524 if ( elemInSet.GetElem() != curElem ) // coincident elem
7526 int& iG = elemInSet.GroupID();
7529 iG = arrayOfGroups.size();
7530 arrayOfGroups.push_back( groupOfElems );
7531 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7533 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7537 groupOfElems.clear();
7538 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7539 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7541 if ( groupIt->size() > 1 ) {
7542 //groupOfElems.sort(); -- theElements are sorted already
7543 theGroupsOfElementsID.emplace_back( *groupIt );
7548 //=======================================================================
7549 //function : MergeElements
7550 //purpose : In each given group, substitute all elements by the first one.
7551 //=======================================================================
7553 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7557 typedef list<int> TListOfIDs;
7558 TListOfIDs rmElemIds; // IDs of elems to remove
7560 SMESHDS_Mesh* aMesh = GetMeshDS();
7562 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7563 while ( groupsIt != theGroupsOfElementsID.end() ) {
7564 TListOfIDs& aGroupOfElemID = *groupsIt;
7565 aGroupOfElemID.sort();
7566 int elemIDToKeep = aGroupOfElemID.front();
7567 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7568 aGroupOfElemID.pop_front();
7569 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7570 while ( idIt != aGroupOfElemID.end() ) {
7571 int elemIDToRemove = *idIt;
7572 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7573 // add the kept element in groups of removed one (PAL15188)
7574 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7575 rmElemIds.push_back( elemIDToRemove );
7581 Remove( rmElemIds, false );
7584 //=======================================================================
7585 //function : MergeEqualElements
7586 //purpose : Remove all but one of elements built on the same nodes.
7587 //=======================================================================
7589 void SMESH_MeshEditor::MergeEqualElements()
7591 TIDSortedElemSet aMeshElements; /* empty input ==
7592 to merge equal elements in the whole mesh */
7593 TListOfListOfElementsID aGroupsOfElementsID;
7594 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7595 MergeElements( aGroupsOfElementsID );
7598 //=======================================================================
7599 //function : findAdjacentFace
7601 //=======================================================================
7603 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7604 const SMDS_MeshNode* n2,
7605 const SMDS_MeshElement* elem)
7607 TIDSortedElemSet elemSet, avoidSet;
7609 avoidSet.insert ( elem );
7610 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7613 //=======================================================================
7614 //function : findSegment
7615 //purpose : Return a mesh segment by two nodes one of which can be medium
7616 //=======================================================================
7618 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7619 const SMDS_MeshNode* n2)
7621 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7622 while ( it->more() )
7624 const SMDS_MeshElement* seg = it->next();
7625 if ( seg->GetNodeIndex( n2 ) >= 0 )
7631 //=======================================================================
7632 //function : FindFreeBorder
7634 //=======================================================================
7636 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7638 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7639 const SMDS_MeshNode* theSecondNode,
7640 const SMDS_MeshNode* theLastNode,
7641 list< const SMDS_MeshNode* > & theNodes,
7642 list< const SMDS_MeshElement* >& theFaces)
7644 if ( !theFirstNode || !theSecondNode )
7646 // find border face between theFirstNode and theSecondNode
7647 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7651 theFaces.push_back( curElem );
7652 theNodes.push_back( theFirstNode );
7653 theNodes.push_back( theSecondNode );
7655 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7656 //TIDSortedElemSet foundElems;
7657 bool needTheLast = ( theLastNode != 0 );
7659 vector<const SMDS_MeshNode*> nodes;
7661 while ( nStart != theLastNode ) {
7662 if ( nStart == theFirstNode )
7663 return !needTheLast;
7665 // find all free border faces sharing nStart
7667 list< const SMDS_MeshElement* > curElemList;
7668 list< const SMDS_MeshNode* > nStartList;
7669 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7670 while ( invElemIt->more() ) {
7671 const SMDS_MeshElement* e = invElemIt->next();
7672 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7675 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7676 SMDS_MeshElement::iterator() );
7677 nodes.push_back( nodes[ 0 ]);
7680 int iNode = 0, nbNodes = nodes.size() - 1;
7681 for ( iNode = 0; iNode < nbNodes; iNode++ )
7682 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7683 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7684 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7686 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7687 curElemList.push_back( e );
7691 // analyse the found
7693 int nbNewBorders = curElemList.size();
7694 if ( nbNewBorders == 0 ) {
7695 // no free border furthermore
7696 return !needTheLast;
7698 else if ( nbNewBorders == 1 ) {
7699 // one more element found
7701 nStart = nStartList.front();
7702 curElem = curElemList.front();
7703 theFaces.push_back( curElem );
7704 theNodes.push_back( nStart );
7707 // several continuations found
7708 list< const SMDS_MeshElement* >::iterator curElemIt;
7709 list< const SMDS_MeshNode* >::iterator nStartIt;
7710 // check if one of them reached the last node
7711 if ( needTheLast ) {
7712 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7713 curElemIt!= curElemList.end();
7714 curElemIt++, nStartIt++ )
7715 if ( *nStartIt == theLastNode ) {
7716 theFaces.push_back( *curElemIt );
7717 theNodes.push_back( *nStartIt );
7721 // find the best free border by the continuations
7722 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7723 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7724 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7725 curElemIt!= curElemList.end();
7726 curElemIt++, nStartIt++ )
7728 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7729 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7730 // find one more free border
7731 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7735 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7736 // choice: clear a worse one
7737 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7738 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7739 contNodes[ iWorse ].clear();
7740 contFaces[ iWorse ].clear();
7743 if ( contNodes[0].empty() && contNodes[1].empty() )
7746 // push_back the best free border
7747 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7748 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7749 //theNodes.pop_back(); // remove nIgnore
7750 theNodes.pop_back(); // remove nStart
7751 //theFaces.pop_back(); // remove curElem
7752 theNodes.splice( theNodes.end(), *cNL );
7753 theFaces.splice( theFaces.end(), *cFL );
7756 } // several continuations found
7757 } // while ( nStart != theLastNode )
7762 //=======================================================================
7763 //function : CheckFreeBorderNodes
7764 //purpose : Return true if the tree nodes are on a free border
7765 //=======================================================================
7767 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7768 const SMDS_MeshNode* theNode2,
7769 const SMDS_MeshNode* theNode3)
7771 list< const SMDS_MeshNode* > nodes;
7772 list< const SMDS_MeshElement* > faces;
7773 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7776 //=======================================================================
7777 //function : SewFreeBorder
7779 //warning : for border-to-side sewing theSideSecondNode is considered as
7780 // the last side node and theSideThirdNode is not used
7781 //=======================================================================
7783 SMESH_MeshEditor::Sew_Error
7784 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7785 const SMDS_MeshNode* theBordSecondNode,
7786 const SMDS_MeshNode* theBordLastNode,
7787 const SMDS_MeshNode* theSideFirstNode,
7788 const SMDS_MeshNode* theSideSecondNode,
7789 const SMDS_MeshNode* theSideThirdNode,
7790 const bool theSideIsFreeBorder,
7791 const bool toCreatePolygons,
7792 const bool toCreatePolyedrs)
7796 Sew_Error aResult = SEW_OK;
7798 // ====================================
7799 // find side nodes and elements
7800 // ====================================
7802 list< const SMDS_MeshNode* > nSide[ 2 ];
7803 list< const SMDS_MeshElement* > eSide[ 2 ];
7804 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7805 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7809 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7810 nSide[0], eSide[0])) {
7811 MESSAGE(" Free Border 1 not found " );
7812 aResult = SEW_BORDER1_NOT_FOUND;
7814 if (theSideIsFreeBorder) {
7817 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7818 nSide[1], eSide[1])) {
7819 MESSAGE(" Free Border 2 not found " );
7820 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7823 if ( aResult != SEW_OK )
7826 if (!theSideIsFreeBorder) {
7830 // -------------------------------------------------------------------------
7832 // 1. If nodes to merge are not coincident, move nodes of the free border
7833 // from the coord sys defined by the direction from the first to last
7834 // nodes of the border to the correspondent sys of the side 2
7835 // 2. On the side 2, find the links most co-directed with the correspondent
7836 // links of the free border
7837 // -------------------------------------------------------------------------
7839 // 1. Since sewing may break if there are volumes to split on the side 2,
7840 // we won't move nodes but just compute new coordinates for them
7841 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7842 TNodeXYZMap nBordXYZ;
7843 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7844 list< const SMDS_MeshNode* >::iterator nBordIt;
7846 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7847 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7848 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7849 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7850 double tol2 = 1.e-8;
7851 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7852 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7853 // Need node movement.
7855 // find X and Z axes to create trsf
7856 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7858 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7860 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7863 gp_Ax3 toBordAx( Pb1, Zb, X );
7864 gp_Ax3 fromSideAx( Ps1, Zs, X );
7865 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7867 gp_Trsf toBordSys, fromSide2Sys;
7868 toBordSys.SetTransformation( toBordAx );
7869 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7870 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7873 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7874 const SMDS_MeshNode* n = *nBordIt;
7875 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7876 toBordSys.Transforms( xyz );
7877 fromSide2Sys.Transforms( xyz );
7878 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7882 // just insert nodes XYZ in the nBordXYZ map
7883 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7884 const SMDS_MeshNode* n = *nBordIt;
7885 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7889 // 2. On the side 2, find the links most co-directed with the correspondent
7890 // links of the free border
7892 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7893 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7894 sideNodes.push_back( theSideFirstNode );
7896 bool hasVolumes = false;
7897 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7898 set<long> foundSideLinkIDs, checkedLinkIDs;
7899 SMDS_VolumeTool volume;
7900 //const SMDS_MeshNode* faceNodes[ 4 ];
7902 const SMDS_MeshNode* sideNode;
7903 const SMDS_MeshElement* sideElem = 0;
7904 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7905 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7906 nBordIt = bordNodes.begin();
7908 // border node position and border link direction to compare with
7909 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7910 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7911 // choose next side node by link direction or by closeness to
7912 // the current border node:
7913 bool searchByDir = ( *nBordIt != theBordLastNode );
7915 // find the next node on the Side 2
7917 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7919 checkedLinkIDs.clear();
7920 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7922 // loop on inverse elements of current node (prevSideNode) on the Side 2
7923 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7924 while ( invElemIt->more() )
7926 const SMDS_MeshElement* elem = invElemIt->next();
7927 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7928 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7929 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7930 bool isVolume = volume.Set( elem );
7931 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7932 if ( isVolume ) // --volume
7934 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7935 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7936 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7937 while ( nIt->more() ) {
7938 nodes[ iNode ] = cast2Node( nIt->next() );
7939 if ( nodes[ iNode++ ] == prevSideNode )
7940 iPrevNode = iNode - 1;
7942 // there are 2 links to check
7947 // loop on links, to be precise, on the second node of links
7948 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7949 const SMDS_MeshNode* n = nodes[ iNode ];
7951 if ( !volume.IsLinked( n, prevSideNode ))
7955 if ( iNode ) // a node before prevSideNode
7956 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7957 else // a node after prevSideNode
7958 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7960 // check if this link was already used
7961 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7962 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7963 if (!isJustChecked &&
7964 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7966 // test a link geometrically
7967 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7968 bool linkIsBetter = false;
7969 double dot = 0.0, dist = 0.0;
7970 if ( searchByDir ) { // choose most co-directed link
7971 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7972 linkIsBetter = ( dot > maxDot );
7974 else { // choose link with the node closest to bordPos
7975 dist = ( nextXYZ - bordPos ).SquareModulus();
7976 linkIsBetter = ( dist < minDist );
7978 if ( linkIsBetter ) {
7987 } // loop on inverse elements of prevSideNode
7990 MESSAGE(" Can't find path by links of the Side 2 ");
7991 return SEW_BAD_SIDE_NODES;
7993 sideNodes.push_back( sideNode );
7994 sideElems.push_back( sideElem );
7995 foundSideLinkIDs.insert ( linkID );
7996 prevSideNode = sideNode;
7998 if ( *nBordIt == theBordLastNode )
7999 searchByDir = false;
8001 // find the next border link to compare with
8002 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8003 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8004 // move to next border node if sideNode is before forward border node (bordPos)
8005 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8006 prevBordNode = *nBordIt;
8008 bordPos = nBordXYZ[ *nBordIt ];
8009 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8010 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8014 while ( sideNode != theSideSecondNode );
8016 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8017 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8018 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8020 } // end nodes search on the side 2
8022 // ============================
8023 // sew the border to the side 2
8024 // ============================
8026 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8027 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8029 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8030 if ( toMergeConformal && toCreatePolygons )
8032 // do not merge quadrangles if polygons are OK (IPAL0052824)
8033 eIt[0] = eSide[0].begin();
8034 eIt[1] = eSide[1].begin();
8035 bool allQuads[2] = { true, true };
8036 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8037 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8038 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8040 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8043 TListOfListOfNodes nodeGroupsToMerge;
8044 if (( toMergeConformal ) ||
8045 ( theSideIsFreeBorder && !theSideThirdNode )) {
8047 // all nodes are to be merged
8049 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8050 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8051 nIt[0]++, nIt[1]++ )
8053 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8054 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8055 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8060 // insert new nodes into the border and the side to get equal nb of segments
8062 // get normalized parameters of nodes on the borders
8063 vector< double > param[ 2 ];
8064 param[0].resize( maxNbNodes );
8065 param[1].resize( maxNbNodes );
8067 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8068 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8069 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8070 const SMDS_MeshNode* nPrev = *nIt;
8071 double bordLength = 0;
8072 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8073 const SMDS_MeshNode* nCur = *nIt;
8074 gp_XYZ segment (nCur->X() - nPrev->X(),
8075 nCur->Y() - nPrev->Y(),
8076 nCur->Z() - nPrev->Z());
8077 double segmentLen = segment.Modulus();
8078 bordLength += segmentLen;
8079 param[ iBord ][ iNode ] = bordLength;
8082 // normalize within [0,1]
8083 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8084 param[ iBord ][ iNode ] /= bordLength;
8088 // loop on border segments
8089 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8090 int i[ 2 ] = { 0, 0 };
8091 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8092 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8094 // element can be split while iterating on border if it has two edges in the border
8095 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8096 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8098 TElemOfNodeListMap insertMap;
8099 TElemOfNodeListMap::iterator insertMapIt;
8101 // key: elem to insert nodes into
8102 // value: 2 nodes to insert between + nodes to be inserted
8104 bool next[ 2 ] = { false, false };
8106 // find min adjacent segment length after sewing
8107 double nextParam = 10., prevParam = 0;
8108 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8109 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8110 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8111 if ( i[ iBord ] > 0 )
8112 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8114 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8115 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8116 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8118 // choose to insert or to merge nodes
8119 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8120 if ( Abs( du ) <= minSegLen * 0.2 ) {
8123 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8124 const SMDS_MeshNode* n0 = *nIt[0];
8125 const SMDS_MeshNode* n1 = *nIt[1];
8126 nodeGroupsToMerge.back().push_back( n1 );
8127 nodeGroupsToMerge.back().push_back( n0 );
8128 // position of node of the border changes due to merge
8129 param[ 0 ][ i[0] ] += du;
8130 // move n1 for the sake of elem shape evaluation during insertion.
8131 // n1 will be removed by MergeNodes() anyway
8132 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8133 next[0] = next[1] = true;
8138 int intoBord = ( du < 0 ) ? 0 : 1;
8139 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8140 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8141 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8142 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8143 if ( intoBord == 1 ) {
8144 // move node of the border to be on a link of elem of the side
8145 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8146 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8147 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8148 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8150 elemReplaceMapIt = elemReplaceMap.find( elem );
8151 if ( elemReplaceMapIt != elemReplaceMap.end() )
8152 elem = elemReplaceMapIt->second;
8154 insertMapIt = insertMap.find( elem );
8155 bool notFound = ( insertMapIt == insertMap.end() );
8156 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8158 // insert into another link of the same element:
8159 // 1. perform insertion into the other link of the elem
8160 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8161 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8162 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8163 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8164 // 2. perform insertion into the link of adjacent faces
8165 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8166 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8168 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8169 InsertNodesIntoLink( seg, n12, n22, nodeList );
8171 if (toCreatePolyedrs) {
8172 // perform insertion into the links of adjacent volumes
8173 UpdateVolumes(n12, n22, nodeList);
8175 // 3. find an element appeared on n1 and n2 after the insertion
8176 insertMap.erase( insertMapIt );
8177 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8178 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8181 if ( notFound || otherLink ) {
8182 // add element and nodes of the side into the insertMap
8183 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8184 (*insertMapIt).second.push_back( n1 );
8185 (*insertMapIt).second.push_back( n2 );
8187 // add node to be inserted into elem
8188 (*insertMapIt).second.push_back( nIns );
8189 next[ 1 - intoBord ] = true;
8192 // go to the next segment
8193 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8194 if ( next[ iBord ] ) {
8195 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8197 nPrev[ iBord ] = *nIt[ iBord ];
8198 nIt[ iBord ]++; i[ iBord ]++;
8202 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8204 // perform insertion of nodes into elements
8206 for (insertMapIt = insertMap.begin();
8207 insertMapIt != insertMap.end();
8210 const SMDS_MeshElement* elem = (*insertMapIt).first;
8211 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8212 if ( nodeList.size() < 3 ) continue;
8213 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8214 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8216 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8218 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8219 InsertNodesIntoLink( seg, n1, n2, nodeList );
8222 if ( !theSideIsFreeBorder ) {
8223 // look for and insert nodes into the faces adjacent to elem
8224 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8225 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8228 if (toCreatePolyedrs) {
8229 // perform insertion into the links of adjacent volumes
8230 UpdateVolumes(n1, n2, nodeList);
8233 } // end: insert new nodes
8235 MergeNodes ( nodeGroupsToMerge );
8238 // Remove coincident segments
8241 TIDSortedElemSet segments;
8242 SMESH_SequenceOfElemPtr newFaces;
8243 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8245 if ( !myLastCreatedElems[i] ) continue;
8246 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8247 segments.insert( segments.end(), myLastCreatedElems[i] );
8249 newFaces.push_back( myLastCreatedElems[i] );
8251 // get segments adjacent to merged nodes
8252 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8253 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8255 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8256 if ( nodes.front()->IsNull() ) continue;
8257 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8258 while ( segIt->more() )
8259 segments.insert( segIt->next() );
8263 TListOfListOfElementsID equalGroups;
8264 if ( !segments.empty() )
8265 FindEqualElements( segments, equalGroups );
8266 if ( !equalGroups.empty() )
8268 // remove from segments those that will be removed
8269 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8270 for ( ; itGroups != equalGroups.end(); ++itGroups )
8272 list< int >& group = *itGroups;
8273 list< int >::iterator id = group.begin();
8274 for ( ++id; id != group.end(); ++id )
8275 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8276 segments.erase( seg );
8278 // remove equal segments
8279 MergeElements( equalGroups );
8281 // restore myLastCreatedElems
8282 myLastCreatedElems = newFaces;
8283 TIDSortedElemSet::iterator seg = segments.begin();
8284 for ( ; seg != segments.end(); ++seg )
8285 myLastCreatedElems.push_back( *seg );
8291 //=======================================================================
8292 //function : InsertNodesIntoLink
8293 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8294 // and theBetweenNode2 and split theElement
8295 //=======================================================================
8297 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8298 const SMDS_MeshNode* theBetweenNode1,
8299 const SMDS_MeshNode* theBetweenNode2,
8300 list<const SMDS_MeshNode*>& theNodesToInsert,
8301 const bool toCreatePoly)
8303 if ( !theElement ) return;
8305 SMESHDS_Mesh *aMesh = GetMeshDS();
8306 vector<const SMDS_MeshElement*> newElems;
8308 if ( theElement->GetType() == SMDSAbs_Edge )
8310 theNodesToInsert.push_front( theBetweenNode1 );
8311 theNodesToInsert.push_back ( theBetweenNode2 );
8312 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8313 const SMDS_MeshNode* n1 = *n;
8314 for ( ++n; n != theNodesToInsert.end(); ++n )
8316 const SMDS_MeshNode* n2 = *n;
8317 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8318 AddToSameGroups( seg, theElement, aMesh );
8320 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8323 theNodesToInsert.pop_front();
8324 theNodesToInsert.pop_back();
8326 if ( theElement->IsQuadratic() ) // add a not split part
8328 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8329 theElement->end_nodes() );
8330 int iOther = 0, nbN = nodes.size();
8331 for ( ; iOther < nbN; ++iOther )
8332 if ( nodes[iOther] != theBetweenNode1 &&
8333 nodes[iOther] != theBetweenNode2 )
8337 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8338 AddToSameGroups( seg, theElement, aMesh );
8340 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8342 else if ( iOther == 2 )
8344 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8345 AddToSameGroups( seg, theElement, aMesh );
8347 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8350 // treat new elements
8351 for ( size_t i = 0; i < newElems.size(); ++i )
8354 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8355 myLastCreatedElems.push_back( newElems[i] );
8357 ReplaceElemInGroups( theElement, newElems, aMesh );
8358 aMesh->RemoveElement( theElement );
8361 } // if ( theElement->GetType() == SMDSAbs_Edge )
8363 const SMDS_MeshElement* theFace = theElement;
8364 if ( theFace->GetType() != SMDSAbs_Face ) return;
8366 // find indices of 2 link nodes and of the rest nodes
8367 int iNode = 0, il1, il2, i3, i4;
8368 il1 = il2 = i3 = i4 = -1;
8369 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8371 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8372 while ( nodeIt->more() ) {
8373 const SMDS_MeshNode* n = nodeIt->next();
8374 if ( n == theBetweenNode1 )
8376 else if ( n == theBetweenNode2 )
8382 nodes[ iNode++ ] = n;
8384 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8387 // arrange link nodes to go one after another regarding the face orientation
8388 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8389 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8394 aNodesToInsert.reverse();
8396 // check that not link nodes of a quadrangles are in good order
8397 int nbFaceNodes = theFace->NbNodes();
8398 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8404 if (toCreatePoly || theFace->IsPoly()) {
8407 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8409 // add nodes of face up to first node of link
8411 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8412 while ( nodeIt->more() && !isFLN ) {
8413 const SMDS_MeshNode* n = nodeIt->next();
8414 poly_nodes[iNode++] = n;
8415 isFLN = ( n == nodes[il1] );
8417 // add nodes to insert
8418 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8419 for (; nIt != aNodesToInsert.end(); nIt++) {
8420 poly_nodes[iNode++] = *nIt;
8422 // add nodes of face starting from last node of link
8423 while ( nodeIt->more() ) {
8424 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8425 poly_nodes[iNode++] = n;
8429 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8432 else if ( !theFace->IsQuadratic() )
8434 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8435 int nbLinkNodes = 2 + aNodesToInsert.size();
8436 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8437 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8438 linkNodes[ 0 ] = nodes[ il1 ];
8439 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8440 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8441 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8442 linkNodes[ iNode++ ] = *nIt;
8444 // decide how to split a quadrangle: compare possible variants
8445 // and choose which of splits to be a quadrangle
8446 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8447 if ( nbFaceNodes == 3 ) {
8448 iBestQuad = nbSplits;
8451 else if ( nbFaceNodes == 4 ) {
8452 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8453 double aBestRate = DBL_MAX;
8454 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8456 double aBadRate = 0;
8457 // evaluate elements quality
8458 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8459 if ( iSplit == iQuad ) {
8460 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8464 aBadRate += getBadRate( &quad, aCrit );
8467 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8469 nodes[ iSplit < iQuad ? i4 : i3 ]);
8470 aBadRate += getBadRate( &tria, aCrit );
8474 if ( aBadRate < aBestRate ) {
8476 aBestRate = aBadRate;
8481 // create new elements
8483 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8485 if ( iSplit == iBestQuad )
8486 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8491 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8493 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8496 const SMDS_MeshNode* newNodes[ 4 ];
8497 newNodes[ 0 ] = linkNodes[ i1 ];
8498 newNodes[ 1 ] = linkNodes[ i2 ];
8499 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8500 newNodes[ 3 ] = nodes[ i4 ];
8501 if (iSplit == iBestQuad)
8502 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8504 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8506 } // end if(!theFace->IsQuadratic())
8508 else { // theFace is quadratic
8509 // we have to split theFace on simple triangles and one simple quadrangle
8511 int nbshift = tmp*2;
8512 // shift nodes in nodes[] by nbshift
8514 for(i=0; i<nbshift; i++) {
8515 const SMDS_MeshNode* n = nodes[0];
8516 for(j=0; j<nbFaceNodes-1; j++) {
8517 nodes[j] = nodes[j+1];
8519 nodes[nbFaceNodes-1] = n;
8521 il1 = il1 - nbshift;
8522 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8523 // n0 n1 n2 n0 n1 n2
8524 // +-----+-----+ +-----+-----+
8533 // create new elements
8535 if ( nbFaceNodes == 6 ) { // quadratic triangle
8536 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8537 if ( theFace->IsMediumNode(nodes[il1]) ) {
8538 // create quadrangle
8539 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8545 // create quadrangle
8546 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8552 else { // nbFaceNodes==8 - quadratic quadrangle
8553 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8554 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8555 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8556 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8557 // create quadrangle
8558 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8564 // create quadrangle
8565 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8571 // create needed triangles using n1,n2,n3 and inserted nodes
8572 int nbn = 2 + aNodesToInsert.size();
8573 vector<const SMDS_MeshNode*> aNodes(nbn);
8574 aNodes[0 ] = nodes[n1];
8575 aNodes[nbn-1] = nodes[n2];
8576 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8577 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8578 aNodes[iNode++] = *nIt;
8580 for ( i = 1; i < nbn; i++ )
8581 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8584 // remove the old face
8585 for ( size_t i = 0; i < newElems.size(); ++i )
8588 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8589 myLastCreatedElems.push_back( newElems[i] );
8591 ReplaceElemInGroups( theFace, newElems, aMesh );
8592 aMesh->RemoveElement(theFace);
8594 } // InsertNodesIntoLink()
8596 //=======================================================================
8597 //function : UpdateVolumes
8599 //=======================================================================
8601 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8602 const SMDS_MeshNode* theBetweenNode2,
8603 list<const SMDS_MeshNode*>& theNodesToInsert)
8607 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8608 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8609 const SMDS_MeshElement* elem = invElemIt->next();
8611 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8612 SMDS_VolumeTool aVolume (elem);
8613 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8616 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8617 int iface, nbFaces = aVolume.NbFaces();
8618 vector<const SMDS_MeshNode *> poly_nodes;
8619 vector<int> quantities (nbFaces);
8621 for (iface = 0; iface < nbFaces; iface++) {
8622 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8623 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8624 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8626 for (int inode = 0; inode < nbFaceNodes; inode++) {
8627 poly_nodes.push_back(faceNodes[inode]);
8629 if (nbInserted == 0) {
8630 if (faceNodes[inode] == theBetweenNode1) {
8631 if (faceNodes[inode + 1] == theBetweenNode2) {
8632 nbInserted = theNodesToInsert.size();
8634 // add nodes to insert
8635 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8636 for (; nIt != theNodesToInsert.end(); nIt++) {
8637 poly_nodes.push_back(*nIt);
8641 else if (faceNodes[inode] == theBetweenNode2) {
8642 if (faceNodes[inode + 1] == theBetweenNode1) {
8643 nbInserted = theNodesToInsert.size();
8645 // add nodes to insert in reversed order
8646 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8648 for (; nIt != theNodesToInsert.begin(); nIt--) {
8649 poly_nodes.push_back(*nIt);
8651 poly_nodes.push_back(*nIt);
8658 quantities[iface] = nbFaceNodes + nbInserted;
8661 // Replace the volume
8662 SMESHDS_Mesh *aMesh = GetMeshDS();
8664 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8666 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8667 myLastCreatedElems.push_back( newElem );
8668 ReplaceElemInGroups( elem, newElem, aMesh );
8670 aMesh->RemoveElement( elem );
8676 //================================================================================
8678 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8680 //================================================================================
8682 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8683 vector<const SMDS_MeshNode *> & nodes,
8684 vector<int> & nbNodeInFaces )
8687 nbNodeInFaces.clear();
8688 SMDS_VolumeTool vTool ( elem );
8689 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8691 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8692 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8693 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8698 //=======================================================================
8700 * \brief Convert elements contained in a sub-mesh to quadratic
8701 * \return int - nb of checked elements
8703 //=======================================================================
8705 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8706 SMESH_MesherHelper& theHelper,
8707 const bool theForce3d)
8709 //MESSAGE("convertElemToQuadratic");
8711 if( !theSm ) return nbElem;
8713 vector<int> nbNodeInFaces;
8714 vector<const SMDS_MeshNode *> nodes;
8715 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8716 while(ElemItr->more())
8719 const SMDS_MeshElement* elem = ElemItr->next();
8720 if( !elem ) continue;
8722 // analyse a necessity of conversion
8723 const SMDSAbs_ElementType aType = elem->GetType();
8724 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8726 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8727 bool hasCentralNodes = false;
8728 if ( elem->IsQuadratic() )
8731 switch ( aGeomType ) {
8732 case SMDSEntity_Quad_Triangle:
8733 case SMDSEntity_Quad_Quadrangle:
8734 case SMDSEntity_Quad_Hexa:
8735 case SMDSEntity_Quad_Penta:
8736 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8738 case SMDSEntity_BiQuad_Triangle:
8739 case SMDSEntity_BiQuad_Quadrangle:
8740 case SMDSEntity_TriQuad_Hexa:
8741 case SMDSEntity_BiQuad_Penta:
8742 alreadyOK = theHelper.GetIsBiQuadratic();
8743 hasCentralNodes = true;
8748 // take into account already present medium nodes
8750 case SMDSAbs_Volume:
8751 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8753 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8755 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8761 // get elem data needed to re-create it
8763 const int id = elem->GetID();
8764 const int nbNodes = elem->NbCornerNodes();
8765 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8766 if ( aGeomType == SMDSEntity_Polyhedra )
8767 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8768 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8769 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8771 // remove a linear element
8772 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8774 // remove central nodes of biquadratic elements (biquad->quad conversion)
8775 if ( hasCentralNodes )
8776 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8777 if ( nodes[i]->NbInverseElements() == 0 )
8778 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8780 const SMDS_MeshElement* NewElem = 0;
8786 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8794 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8797 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8800 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8804 case SMDSAbs_Volume :
8808 case SMDSEntity_Tetra:
8809 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8811 case SMDSEntity_Pyramid:
8812 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8814 case SMDSEntity_Penta:
8815 case SMDSEntity_Quad_Penta:
8816 case SMDSEntity_BiQuad_Penta:
8817 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8819 case SMDSEntity_Hexa:
8820 case SMDSEntity_Quad_Hexa:
8821 case SMDSEntity_TriQuad_Hexa:
8822 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8823 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8825 case SMDSEntity_Hexagonal_Prism:
8827 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8834 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8835 if( NewElem && NewElem->getshapeId() < 1 )
8836 theSm->AddElement( NewElem );
8840 //=======================================================================
8841 //function : ConvertToQuadratic
8843 //=======================================================================
8845 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8847 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8848 SMESHDS_Mesh* meshDS = GetMeshDS();
8850 SMESH_MesherHelper aHelper(*myMesh);
8852 aHelper.SetIsQuadratic( true );
8853 aHelper.SetIsBiQuadratic( theToBiQuad );
8854 aHelper.SetElementsOnShape(true);
8855 aHelper.ToFixNodeParameters( true );
8857 // convert elements assigned to sub-meshes
8858 int nbCheckedElems = 0;
8859 if ( myMesh->HasShapeToMesh() )
8861 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8863 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8864 while ( smIt->more() ) {
8865 SMESH_subMesh* sm = smIt->next();
8866 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8867 aHelper.SetSubShape( sm->GetSubShape() );
8868 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8874 // convert elements NOT assigned to sub-meshes
8875 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8876 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8878 aHelper.SetElementsOnShape(false);
8879 SMESHDS_SubMesh *smDS = 0;
8882 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8883 while( aEdgeItr->more() )
8885 const SMDS_MeshEdge* edge = aEdgeItr->next();
8886 if ( !edge->IsQuadratic() )
8888 int id = edge->GetID();
8889 const SMDS_MeshNode* n1 = edge->GetNode(0);
8890 const SMDS_MeshNode* n2 = edge->GetNode(1);
8892 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8894 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8895 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8899 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8904 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8905 while( aFaceItr->more() )
8907 const SMDS_MeshFace* face = aFaceItr->next();
8908 if ( !face ) continue;
8910 const SMDSAbs_EntityType type = face->GetEntityType();
8914 case SMDSEntity_Quad_Triangle:
8915 case SMDSEntity_Quad_Quadrangle:
8916 alreadyOK = !theToBiQuad;
8917 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8919 case SMDSEntity_BiQuad_Triangle:
8920 case SMDSEntity_BiQuad_Quadrangle:
8921 alreadyOK = theToBiQuad;
8922 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8924 default: alreadyOK = false;
8929 const int id = face->GetID();
8930 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8932 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8934 SMDS_MeshFace * NewFace = 0;
8937 case SMDSEntity_Triangle:
8938 case SMDSEntity_Quad_Triangle:
8939 case SMDSEntity_BiQuad_Triangle:
8940 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8941 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8942 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8945 case SMDSEntity_Quadrangle:
8946 case SMDSEntity_Quad_Quadrangle:
8947 case SMDSEntity_BiQuad_Quadrangle:
8948 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8949 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8950 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8954 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8956 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8960 vector<int> nbNodeInFaces;
8961 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8962 while(aVolumeItr->more())
8964 const SMDS_MeshVolume* volume = aVolumeItr->next();
8965 if ( !volume ) continue;
8967 const SMDSAbs_EntityType type = volume->GetEntityType();
8968 if ( volume->IsQuadratic() )
8973 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8974 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8975 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8976 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8977 default: alreadyOK = true;
8981 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8985 const int id = volume->GetID();
8986 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8987 if ( type == SMDSEntity_Polyhedra )
8988 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8989 else if ( type == SMDSEntity_Hexagonal_Prism )
8990 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8992 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8994 SMDS_MeshVolume * NewVolume = 0;
8997 case SMDSEntity_Tetra:
8998 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9000 case SMDSEntity_Hexa:
9001 case SMDSEntity_Quad_Hexa:
9002 case SMDSEntity_TriQuad_Hexa:
9003 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9004 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9005 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9006 if ( nodes[i]->NbInverseElements() == 0 )
9007 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9009 case SMDSEntity_Pyramid:
9010 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9011 nodes[3], nodes[4], id, theForce3d);
9013 case SMDSEntity_Penta:
9014 case SMDSEntity_Quad_Penta:
9015 case SMDSEntity_BiQuad_Penta:
9016 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9017 nodes[3], nodes[4], nodes[5], id, theForce3d);
9018 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9019 if ( nodes[i]->NbInverseElements() == 0 )
9020 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9022 case SMDSEntity_Hexagonal_Prism:
9024 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9026 ReplaceElemInGroups(volume, NewVolume, meshDS);
9031 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9032 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9033 // aHelper.FixQuadraticElements(myError);
9034 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9038 //================================================================================
9040 * \brief Makes given elements quadratic
9041 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9042 * \param theElements - elements to make quadratic
9044 //================================================================================
9046 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9047 TIDSortedElemSet& theElements,
9048 const bool theToBiQuad)
9050 if ( theElements.empty() ) return;
9052 // we believe that all theElements are of the same type
9053 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9055 // get all nodes shared by theElements
9056 TIDSortedNodeSet allNodes;
9057 TIDSortedElemSet::iterator eIt = theElements.begin();
9058 for ( ; eIt != theElements.end(); ++eIt )
9059 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9061 // complete theElements with elements of lower dim whose all nodes are in allNodes
9063 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9064 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9065 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9066 for ( ; nIt != allNodes.end(); ++nIt )
9068 const SMDS_MeshNode* n = *nIt;
9069 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9070 while ( invIt->more() )
9072 const SMDS_MeshElement* e = invIt->next();
9073 const SMDSAbs_ElementType type = e->GetType();
9074 if ( e->IsQuadratic() )
9076 quadAdjacentElems[ type ].insert( e );
9079 switch ( e->GetEntityType() ) {
9080 case SMDSEntity_Quad_Triangle:
9081 case SMDSEntity_Quad_Quadrangle:
9082 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9083 case SMDSEntity_BiQuad_Triangle:
9084 case SMDSEntity_BiQuad_Quadrangle:
9085 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9086 default: alreadyOK = true;
9091 if ( type >= elemType )
9092 continue; // same type or more complex linear element
9094 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9095 continue; // e is already checked
9099 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9100 while ( nodeIt->more() && allIn )
9101 allIn = allNodes.count( nodeIt->next() );
9103 theElements.insert(e );
9107 SMESH_MesherHelper helper(*myMesh);
9108 helper.SetIsQuadratic( true );
9109 helper.SetIsBiQuadratic( theToBiQuad );
9111 // add links of quadratic adjacent elements to the helper
9113 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9114 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9115 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9117 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9119 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9120 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9121 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9123 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9125 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9126 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9127 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9129 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9132 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9134 SMESHDS_Mesh* meshDS = GetMeshDS();
9135 SMESHDS_SubMesh* smDS = 0;
9136 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9138 const SMDS_MeshElement* elem = *eIt;
9141 int nbCentralNodes = 0;
9142 switch ( elem->GetEntityType() ) {
9143 // linear convertible
9144 case SMDSEntity_Edge:
9145 case SMDSEntity_Triangle:
9146 case SMDSEntity_Quadrangle:
9147 case SMDSEntity_Tetra:
9148 case SMDSEntity_Pyramid:
9149 case SMDSEntity_Hexa:
9150 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9151 // quadratic that can become bi-quadratic
9152 case SMDSEntity_Quad_Triangle:
9153 case SMDSEntity_Quad_Quadrangle:
9154 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9156 case SMDSEntity_BiQuad_Triangle:
9157 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9158 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9160 default: alreadyOK = true;
9162 if ( alreadyOK ) continue;
9164 const SMDSAbs_ElementType type = elem->GetType();
9165 const int id = elem->GetID();
9166 const int nbNodes = elem->NbCornerNodes();
9167 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9169 helper.SetSubShape( elem->getshapeId() );
9171 if ( !smDS || !smDS->Contains( elem ))
9172 smDS = meshDS->MeshElements( elem->getshapeId() );
9173 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9175 SMDS_MeshElement * newElem = 0;
9178 case 4: // cases for most frequently used element types go first (for optimization)
9179 if ( type == SMDSAbs_Volume )
9180 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9182 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9185 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9186 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9189 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9192 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9195 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9196 nodes[4], id, theForce3d);
9199 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9200 nodes[4], nodes[5], id, theForce3d);
9204 ReplaceElemInGroups( elem, newElem, meshDS);
9205 if( newElem && smDS )
9206 smDS->AddElement( newElem );
9208 // remove central nodes
9209 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9210 if ( nodes[i]->NbInverseElements() == 0 )
9211 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9213 } // loop on theElements
9216 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9217 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9218 // helper.FixQuadraticElements( myError );
9219 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9223 //=======================================================================
9225 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9226 * \return int - nb of checked elements
9228 //=======================================================================
9230 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9231 SMDS_ElemIteratorPtr theItr,
9232 const int /*theShapeID*/)
9235 SMESHDS_Mesh* meshDS = GetMeshDS();
9236 ElemFeatures elemType;
9237 vector<const SMDS_MeshNode *> nodes;
9239 while( theItr->more() )
9241 const SMDS_MeshElement* elem = theItr->next();
9243 if( elem && elem->IsQuadratic())
9246 int nbCornerNodes = elem->NbCornerNodes();
9247 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9249 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9251 //remove a quadratic element
9252 if ( !theSm || !theSm->Contains( elem ))
9253 theSm = meshDS->MeshElements( elem->getshapeId() );
9254 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9256 // remove medium nodes
9257 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9258 if ( nodes[i]->NbInverseElements() == 0 )
9259 meshDS->RemoveFreeNode( nodes[i], theSm );
9261 // add a linear element
9262 nodes.resize( nbCornerNodes );
9263 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9264 ReplaceElemInGroups(elem, newElem, meshDS);
9265 if( theSm && newElem )
9266 theSm->AddElement( newElem );
9272 //=======================================================================
9273 //function : ConvertFromQuadratic
9275 //=======================================================================
9277 bool SMESH_MeshEditor::ConvertFromQuadratic()
9279 int nbCheckedElems = 0;
9280 if ( myMesh->HasShapeToMesh() )
9282 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9284 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9285 while ( smIt->more() ) {
9286 SMESH_subMesh* sm = smIt->next();
9287 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9288 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9294 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9295 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9297 SMESHDS_SubMesh *aSM = 0;
9298 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9306 //================================================================================
9308 * \brief Return true if all medium nodes of the element are in the node set
9310 //================================================================================
9312 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9314 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9315 if ( !nodeSet.count( elem->GetNode(i) ))
9321 //================================================================================
9323 * \brief Makes given elements linear
9325 //================================================================================
9327 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9329 if ( theElements.empty() ) return;
9331 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9332 set<int> mediumNodeIDs;
9333 TIDSortedElemSet::iterator eIt = theElements.begin();
9334 for ( ; eIt != theElements.end(); ++eIt )
9336 const SMDS_MeshElement* e = *eIt;
9337 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9338 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9341 // replace given elements by linear ones
9342 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9343 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9345 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9346 // except those elements sharing medium nodes of quadratic element whose medium nodes
9347 // are not all in mediumNodeIDs
9349 // get remaining medium nodes
9350 TIDSortedNodeSet mediumNodes;
9351 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9352 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9353 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9354 mediumNodes.insert( mediumNodes.end(), n );
9356 // find more quadratic elements to convert
9357 TIDSortedElemSet moreElemsToConvert;
9358 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9359 for ( ; nIt != mediumNodes.end(); ++nIt )
9361 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9362 while ( invIt->more() )
9364 const SMDS_MeshElement* e = invIt->next();
9365 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9367 // find a more complex element including e and
9368 // whose medium nodes are not in mediumNodes
9369 bool complexFound = false;
9370 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9372 SMDS_ElemIteratorPtr invIt2 =
9373 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9374 while ( invIt2->more() )
9376 const SMDS_MeshElement* eComplex = invIt2->next();
9377 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9379 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9380 if ( nbCommonNodes == e->NbNodes())
9382 complexFound = true;
9383 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9389 if ( !complexFound )
9390 moreElemsToConvert.insert( e );
9394 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9395 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9398 //=======================================================================
9399 //function : SewSideElements
9401 //=======================================================================
9403 SMESH_MeshEditor::Sew_Error
9404 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9405 TIDSortedElemSet& theSide2,
9406 const SMDS_MeshNode* theFirstNode1,
9407 const SMDS_MeshNode* theFirstNode2,
9408 const SMDS_MeshNode* theSecondNode1,
9409 const SMDS_MeshNode* theSecondNode2)
9413 if ( theSide1.size() != theSide2.size() )
9414 return SEW_DIFF_NB_OF_ELEMENTS;
9416 Sew_Error aResult = SEW_OK;
9418 // 1. Build set of faces representing each side
9419 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9420 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9422 // =======================================================================
9423 // 1. Build set of faces representing each side:
9424 // =======================================================================
9425 // a. build set of nodes belonging to faces
9426 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9427 // c. create temporary faces representing side of volumes if correspondent
9428 // face does not exist
9430 SMESHDS_Mesh* aMesh = GetMeshDS();
9431 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9432 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9433 TIDSortedElemSet faceSet1, faceSet2;
9434 set<const SMDS_MeshElement*> volSet1, volSet2;
9435 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9436 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9437 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9438 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9439 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9440 int iSide, iFace, iNode;
9442 list<const SMDS_MeshElement* > tempFaceList;
9443 for ( iSide = 0; iSide < 2; iSide++ ) {
9444 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9445 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9446 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9447 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9448 set<const SMDS_MeshElement*>::iterator vIt;
9449 TIDSortedElemSet::iterator eIt;
9450 set<const SMDS_MeshNode*>::iterator nIt;
9452 // check that given nodes belong to given elements
9453 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9454 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9455 int firstIndex = -1, secondIndex = -1;
9456 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9457 const SMDS_MeshElement* elem = *eIt;
9458 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9459 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9460 if ( firstIndex > -1 && secondIndex > -1 ) break;
9462 if ( firstIndex < 0 || secondIndex < 0 ) {
9463 // we can simply return until temporary faces created
9464 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9467 // -----------------------------------------------------------
9468 // 1a. Collect nodes of existing faces
9469 // and build set of face nodes in order to detect missing
9470 // faces corresponding to sides of volumes
9471 // -----------------------------------------------------------
9473 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9475 // loop on the given element of a side
9476 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9477 //const SMDS_MeshElement* elem = *eIt;
9478 const SMDS_MeshElement* elem = *eIt;
9479 if ( elem->GetType() == SMDSAbs_Face ) {
9480 faceSet->insert( elem );
9481 set <const SMDS_MeshNode*> faceNodeSet;
9482 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9483 while ( nodeIt->more() ) {
9484 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9485 nodeSet->insert( n );
9486 faceNodeSet.insert( n );
9488 setOfFaceNodeSet.insert( faceNodeSet );
9490 else if ( elem->GetType() == SMDSAbs_Volume )
9491 volSet->insert( elem );
9493 // ------------------------------------------------------------------------------
9494 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9495 // ------------------------------------------------------------------------------
9497 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9498 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9499 while ( fIt->more() ) { // loop on faces sharing a node
9500 const SMDS_MeshElement* f = fIt->next();
9501 if ( faceSet->find( f ) == faceSet->end() ) {
9502 // check if all nodes are in nodeSet and
9503 // complete setOfFaceNodeSet if they are
9504 set <const SMDS_MeshNode*> faceNodeSet;
9505 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9506 bool allInSet = true;
9507 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9508 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9509 if ( nodeSet->find( n ) == nodeSet->end() )
9512 faceNodeSet.insert( n );
9515 faceSet->insert( f );
9516 setOfFaceNodeSet.insert( faceNodeSet );
9522 // -------------------------------------------------------------------------
9523 // 1c. Create temporary faces representing sides of volumes if correspondent
9524 // face does not exist
9525 // -------------------------------------------------------------------------
9527 if ( !volSet->empty() ) {
9528 //int nodeSetSize = nodeSet->size();
9530 // loop on given volumes
9531 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9532 SMDS_VolumeTool vol (*vIt);
9533 // loop on volume faces: find free faces
9534 // --------------------------------------
9535 list<const SMDS_MeshElement* > freeFaceList;
9536 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9537 if ( !vol.IsFreeFace( iFace ))
9539 // check if there is already a face with same nodes in a face set
9540 const SMDS_MeshElement* aFreeFace = 0;
9541 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9542 int nbNodes = vol.NbFaceNodes( iFace );
9543 set <const SMDS_MeshNode*> faceNodeSet;
9544 vol.GetFaceNodes( iFace, faceNodeSet );
9545 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9547 // no such a face is given but it still can exist, check it
9548 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9549 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9552 // create a temporary face
9553 if ( nbNodes == 3 ) {
9554 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9555 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9557 else if ( nbNodes == 4 ) {
9558 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9559 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9562 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9563 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9564 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9567 tempFaceList.push_back( aFreeFace );
9571 freeFaceList.push_back( aFreeFace );
9573 } // loop on faces of a volume
9575 // choose one of several free faces of a volume
9576 // --------------------------------------------
9577 if ( freeFaceList.size() > 1 ) {
9578 // choose a face having max nb of nodes shared by other elems of a side
9579 int maxNbNodes = -1;
9580 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9581 while ( fIt != freeFaceList.end() ) { // loop on free faces
9582 int nbSharedNodes = 0;
9583 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9584 while ( nodeIt->more() ) { // loop on free face nodes
9585 const SMDS_MeshNode* n =
9586 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9587 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9588 while ( invElemIt->more() ) {
9589 const SMDS_MeshElement* e = invElemIt->next();
9590 nbSharedNodes += faceSet->count( e );
9591 nbSharedNodes += elemSet->count( e );
9594 if ( nbSharedNodes > maxNbNodes ) {
9595 maxNbNodes = nbSharedNodes;
9596 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9598 else if ( nbSharedNodes == maxNbNodes ) {
9602 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9605 if ( freeFaceList.size() > 1 )
9607 // could not choose one face, use another way
9608 // choose a face most close to the bary center of the opposite side
9609 gp_XYZ aBC( 0., 0., 0. );
9610 set <const SMDS_MeshNode*> addedNodes;
9611 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9612 eIt = elemSet2->begin();
9613 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9614 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9615 while ( nodeIt->more() ) { // loop on free face nodes
9616 const SMDS_MeshNode* n =
9617 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9618 if ( addedNodes.insert( n ).second )
9619 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9622 aBC /= addedNodes.size();
9623 double minDist = DBL_MAX;
9624 fIt = freeFaceList.begin();
9625 while ( fIt != freeFaceList.end() ) { // loop on free faces
9627 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9628 while ( nodeIt->more() ) { // loop on free face nodes
9629 const SMDS_MeshNode* n =
9630 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9631 gp_XYZ p( n->X(),n->Y(),n->Z() );
9632 dist += ( aBC - p ).SquareModulus();
9634 if ( dist < minDist ) {
9636 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9639 fIt = freeFaceList.erase( fIt++ );
9642 } // choose one of several free faces of a volume
9644 if ( freeFaceList.size() == 1 ) {
9645 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9646 faceSet->insert( aFreeFace );
9647 // complete a node set with nodes of a found free face
9648 // for ( iNode = 0; iNode < ; iNode++ )
9649 // nodeSet->insert( fNodes[ iNode ] );
9652 } // loop on volumes of a side
9654 // // complete a set of faces if new nodes in a nodeSet appeared
9655 // // ----------------------------------------------------------
9656 // if ( nodeSetSize != nodeSet->size() ) {
9657 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9658 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9659 // while ( fIt->more() ) { // loop on faces sharing a node
9660 // const SMDS_MeshElement* f = fIt->next();
9661 // if ( faceSet->find( f ) == faceSet->end() ) {
9662 // // check if all nodes are in nodeSet and
9663 // // complete setOfFaceNodeSet if they are
9664 // set <const SMDS_MeshNode*> faceNodeSet;
9665 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9666 // bool allInSet = true;
9667 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9668 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9669 // if ( nodeSet->find( n ) == nodeSet->end() )
9670 // allInSet = false;
9672 // faceNodeSet.insert( n );
9674 // if ( allInSet ) {
9675 // faceSet->insert( f );
9676 // setOfFaceNodeSet.insert( faceNodeSet );
9682 } // Create temporary faces, if there are volumes given
9685 if ( faceSet1.size() != faceSet2.size() ) {
9686 // delete temporary faces: they are in reverseElements of actual nodes
9687 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9688 // while ( tmpFaceIt->more() )
9689 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9690 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9691 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9692 // aMesh->RemoveElement(*tmpFaceIt);
9693 MESSAGE("Diff nb of faces");
9694 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9697 // ============================================================
9698 // 2. Find nodes to merge:
9699 // bind a node to remove to a node to put instead
9700 // ============================================================
9702 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9703 if ( theFirstNode1 != theFirstNode2 )
9704 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9705 if ( theSecondNode1 != theSecondNode2 )
9706 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9708 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9709 set< long > linkIdSet; // links to process
9710 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9712 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9713 list< NLink > linkList[2];
9714 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9715 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9716 // loop on links in linkList; find faces by links and append links
9717 // of the found faces to linkList
9718 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9719 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9721 NLink link[] = { *linkIt[0], *linkIt[1] };
9722 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9723 if ( !linkIdSet.count( linkID ) )
9726 // by links, find faces in the face sets,
9727 // and find indices of link nodes in the found faces;
9728 // in a face set, there is only one or no face sharing a link
9729 // ---------------------------------------------------------------
9731 const SMDS_MeshElement* face[] = { 0, 0 };
9732 vector<const SMDS_MeshNode*> fnodes[2];
9733 int iLinkNode[2][2];
9734 TIDSortedElemSet avoidSet;
9735 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9736 const SMDS_MeshNode* n1 = link[iSide].first;
9737 const SMDS_MeshNode* n2 = link[iSide].second;
9738 //cout << "Side " << iSide << " ";
9739 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9740 // find a face by two link nodes
9741 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9742 *faceSetPtr[ iSide ], avoidSet,
9743 &iLinkNode[iSide][0],
9744 &iLinkNode[iSide][1] );
9747 //cout << " F " << face[ iSide]->GetID() <<endl;
9748 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9749 // put face nodes to fnodes
9750 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9751 fnodes[ iSide ].assign( nIt, nEnd );
9752 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9756 // check similarity of elements of the sides
9757 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9758 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9759 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9760 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9763 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9765 break; // do not return because it's necessary to remove tmp faces
9768 // set nodes to merge
9769 // -------------------
9771 if ( face[0] && face[1] ) {
9772 const int nbNodes = face[0]->NbNodes();
9773 if ( nbNodes != face[1]->NbNodes() ) {
9774 MESSAGE("Diff nb of face nodes");
9775 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9776 break; // do not return because it s necessary to remove tmp faces
9778 bool reverse[] = { false, false }; // order of nodes in the link
9779 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9780 // analyse link orientation in faces
9781 int i1 = iLinkNode[ iSide ][ 0 ];
9782 int i2 = iLinkNode[ iSide ][ 1 ];
9783 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9785 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9786 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9787 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9789 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9790 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9793 // add other links of the faces to linkList
9794 // -----------------------------------------
9796 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9797 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9798 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9799 if ( !iter_isnew.second ) { // already in a set: no need to process
9800 linkIdSet.erase( iter_isnew.first );
9802 else // new in set == encountered for the first time: add
9804 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9805 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9806 linkList[0].push_back ( NLink( n1, n2 ));
9807 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9812 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9815 } // loop on link lists
9817 if ( aResult == SEW_OK &&
9818 ( //linkIt[0] != linkList[0].end() ||
9819 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9820 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9821 " " << (faceSetPtr[1]->empty()));
9822 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9825 // ====================================================================
9826 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9827 // ====================================================================
9829 // delete temporary faces
9830 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9831 // while ( tmpFaceIt->more() )
9832 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9833 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9834 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9835 aMesh->RemoveElement(*tmpFaceIt);
9837 if ( aResult != SEW_OK)
9840 list< int > nodeIDsToRemove;
9841 vector< const SMDS_MeshNode*> nodes;
9842 ElemFeatures elemType;
9844 // loop on nodes replacement map
9845 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9846 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9847 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9849 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9850 nodeIDsToRemove.push_back( nToRemove->GetID() );
9851 // loop on elements sharing nToRemove
9852 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9853 while ( invElemIt->more() ) {
9854 const SMDS_MeshElement* e = invElemIt->next();
9855 // get a new suite of nodes: make replacement
9856 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9857 nodes.resize( nbNodes );
9858 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9859 while ( nIt->more() ) {
9860 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9861 nnIt = nReplaceMap.find( n );
9862 if ( nnIt != nReplaceMap.end() ) {
9868 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9869 // elemIDsToRemove.push_back( e->GetID() );
9873 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9874 aMesh->RemoveElement( e );
9876 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9878 AddToSameGroups( newElem, e, aMesh );
9879 if ( int aShapeId = e->getshapeId() )
9880 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9886 Remove( nodeIDsToRemove, true );
9891 //================================================================================
9893 * \brief Find corresponding nodes in two sets of faces
9894 * \param theSide1 - first face set
9895 * \param theSide2 - second first face
9896 * \param theFirstNode1 - a boundary node of set 1
9897 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9898 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9899 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9900 * \param nReplaceMap - output map of corresponding nodes
9901 * \return bool - is a success or not
9903 //================================================================================
9906 //#define DEBUG_MATCHING_NODES
9909 SMESH_MeshEditor::Sew_Error
9910 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9911 set<const SMDS_MeshElement*>& theSide2,
9912 const SMDS_MeshNode* theFirstNode1,
9913 const SMDS_MeshNode* theFirstNode2,
9914 const SMDS_MeshNode* theSecondNode1,
9915 const SMDS_MeshNode* theSecondNode2,
9916 TNodeNodeMap & nReplaceMap)
9918 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9920 nReplaceMap.clear();
9921 //if ( theFirstNode1 != theFirstNode2 )
9922 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9923 //if ( theSecondNode1 != theSecondNode2 )
9924 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9926 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9927 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9929 list< NLink > linkList[2];
9930 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9931 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9933 // loop on links in linkList; find faces by links and append links
9934 // of the found faces to linkList
9935 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9936 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9937 NLink link[] = { *linkIt[0], *linkIt[1] };
9938 if ( linkSet.find( link[0] ) == linkSet.end() )
9941 // by links, find faces in the face sets,
9942 // and find indices of link nodes in the found faces;
9943 // in a face set, there is only one or no face sharing a link
9944 // ---------------------------------------------------------------
9946 const SMDS_MeshElement* face[] = { 0, 0 };
9947 list<const SMDS_MeshNode*> notLinkNodes[2];
9948 //bool reverse[] = { false, false }; // order of notLinkNodes
9950 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9952 const SMDS_MeshNode* n1 = link[iSide].first;
9953 const SMDS_MeshNode* n2 = link[iSide].second;
9954 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9955 set< const SMDS_MeshElement* > facesOfNode1;
9956 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9958 // during a loop of the first node, we find all faces around n1,
9959 // during a loop of the second node, we find one face sharing both n1 and n2
9960 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9961 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9962 while ( fIt->more() ) { // loop on faces sharing a node
9963 const SMDS_MeshElement* f = fIt->next();
9964 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9965 ! facesOfNode1.insert( f ).second ) // f encounters twice
9967 if ( face[ iSide ] ) {
9968 MESSAGE( "2 faces per link " );
9969 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9972 faceSet->erase( f );
9974 // get not link nodes
9975 int nbN = f->NbNodes();
9976 if ( f->IsQuadratic() )
9978 nbNodes[ iSide ] = nbN;
9979 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9980 int i1 = f->GetNodeIndex( n1 );
9981 int i2 = f->GetNodeIndex( n2 );
9982 int iEnd = nbN, iBeg = -1, iDelta = 1;
9983 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9985 std::swap( iEnd, iBeg ); iDelta = -1;
9990 if ( i == iEnd ) i = iBeg + iDelta;
9991 if ( i == i1 ) break;
9992 nodes.push_back ( f->GetNode( i ) );
9998 // check similarity of elements of the sides
9999 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10000 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10001 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10002 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10005 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10009 // set nodes to merge
10010 // -------------------
10012 if ( face[0] && face[1] ) {
10013 if ( nbNodes[0] != nbNodes[1] ) {
10014 MESSAGE("Diff nb of face nodes");
10015 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10017 #ifdef DEBUG_MATCHING_NODES
10018 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10019 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10020 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10022 int nbN = nbNodes[0];
10024 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10025 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10026 for ( int i = 0 ; i < nbN - 2; ++i ) {
10027 #ifdef DEBUG_MATCHING_NODES
10028 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10030 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10034 // add other links of the face 1 to linkList
10035 // -----------------------------------------
10037 const SMDS_MeshElement* f0 = face[0];
10038 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10039 for ( int i = 0; i < nbN; i++ )
10041 const SMDS_MeshNode* n2 = f0->GetNode( i );
10042 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10043 linkSet.insert( SMESH_TLink( n1, n2 ));
10044 if ( !iter_isnew.second ) { // already in a set: no need to process
10045 linkSet.erase( iter_isnew.first );
10047 else // new in set == encountered for the first time: add
10049 #ifdef DEBUG_MATCHING_NODES
10050 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10051 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10053 linkList[0].push_back ( NLink( n1, n2 ));
10054 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10059 } // loop on link lists
10064 namespace // automatically find theAffectedElems for DoubleNodes()
10066 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10068 //--------------------------------------------------------------------------------
10069 // Nodes shared by adjacent FissureBorder's.
10070 // 1 node if FissureBorder separates faces
10071 // 2 nodes if FissureBorder separates volumes
10074 const SMDS_MeshNode* _nodes[2];
10077 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10081 _nbNodes = bool( n1 ) + bool( n2 );
10082 if ( _nbNodes == 2 && n1 > n2 )
10083 std::swap( _nodes[0], _nodes[1] );
10085 bool operator<( const SubBorder& other ) const
10087 for ( int i = 0; i < _nbNodes; ++i )
10089 if ( _nodes[i] < other._nodes[i] ) return true;
10090 if ( _nodes[i] > other._nodes[i] ) return false;
10096 //--------------------------------------------------------------------------------
10097 // Map a SubBorder to all FissureBorder it bounds
10098 struct FissureBorder;
10099 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10100 typedef TBorderLinks::iterator TMappedSub;
10102 //--------------------------------------------------------------------------------
10104 * \brief Element border (volume facet or face edge) at a fissure
10106 struct FissureBorder
10108 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10109 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10111 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10112 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10114 FissureBorder( FissureBorder && from ) // move constructor
10116 std::swap( _nodes, from._nodes );
10117 std::swap( _sortedNodes, from._sortedNodes );
10118 _elems[0] = from._elems[0];
10119 _elems[1] = from._elems[1];
10122 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10123 std::vector< const SMDS_MeshElement* > & adjElems)
10124 : _nodes( elemToDuplicate->NbCornerNodes() )
10126 for ( size_t i = 0; i < _nodes.size(); ++i )
10127 _nodes[i] = elemToDuplicate->GetNode( i );
10129 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10130 findAdjacent( type, adjElems );
10133 FissureBorder( const SMDS_MeshNode** nodes,
10134 const size_t nbNodes,
10135 const SMDSAbs_ElementType adjElemsType,
10136 std::vector< const SMDS_MeshElement* > & adjElems)
10137 : _nodes( nodes, nodes + nbNodes )
10139 findAdjacent( adjElemsType, adjElems );
10142 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10143 std::vector< const SMDS_MeshElement* > & adjElems)
10145 _elems[0] = _elems[1] = 0;
10147 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10148 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10149 _elems[i] = adjElems[i];
10152 bool operator<( const FissureBorder& other ) const
10154 return GetSortedNodes() < other.GetSortedNodes();
10157 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10159 if ( _sortedNodes.empty() && !_nodes.empty() )
10161 FissureBorder* me = const_cast<FissureBorder*>( this );
10162 me->_sortedNodes = me->_nodes;
10163 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10165 return _sortedNodes;
10168 size_t NbSub() const
10170 return _nodes.size();
10173 SubBorder Sub(size_t i) const
10175 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10178 void AddSelfTo( TBorderLinks& borderLinks )
10180 _mappedSubs.resize( NbSub() );
10181 for ( size_t i = 0; i < NbSub(); ++i )
10183 TBorderLinks::iterator s2b =
10184 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10185 s2b->second.push_back( this );
10186 _mappedSubs[ i ] = s2b;
10195 const SMDS_MeshElement* GetMarkedElem() const
10197 if ( _nodes.empty() ) return 0; // cleared
10198 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10199 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10203 gp_XYZ GetNorm() const // normal to the border
10206 if ( _nodes.size() == 2 )
10208 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10209 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10211 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10214 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10215 norm = bordDir ^ avgNorm;
10219 SMESH_NodeXYZ p0( _nodes[0] );
10220 SMESH_NodeXYZ p1( _nodes[1] );
10221 SMESH_NodeXYZ p2( _nodes[2] );
10222 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10224 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10230 void ChooseSide() // mark an _elem located at positive side of fissure
10232 _elems[0]->setIsMarked( true );
10233 gp_XYZ norm = GetNorm();
10234 double maxX = norm.Coord(1);
10235 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10236 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10239 _elems[0]->setIsMarked( false );
10240 _elems[1]->setIsMarked( true );
10244 }; // struct FissureBorder
10246 //--------------------------------------------------------------------------------
10248 * \brief Classifier of elements at fissure edge
10250 class FissureNormal
10252 std::vector< gp_XYZ > _normals;
10256 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10259 _normals.reserve(2);
10260 _normals.push_back( bord.GetNorm() );
10261 if ( _normals.size() == 2 )
10262 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10265 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10268 switch ( _normals.size() ) {
10271 isIn = !isOut( n, _normals[0], elem );
10276 bool in1 = !isOut( n, _normals[0], elem );
10277 bool in2 = !isOut( n, _normals[1], elem );
10278 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10285 //================================================================================
10287 * \brief Classify an element by a plane passing through a node
10289 //================================================================================
10291 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10293 SMESH_NodeXYZ p = n;
10295 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10297 SMESH_NodeXYZ pi = elem->GetNode( i );
10298 sumDot += norm * ( pi - p );
10300 return sumDot < -1e-100;
10303 //================================================================================
10305 * \brief Find FissureBorder's by nodes to duplicate
10307 //================================================================================
10309 void findFissureBorders( const TIDSortedElemSet& theNodes,
10310 std::vector< FissureBorder > & theFissureBorders )
10312 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10313 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10315 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10316 if ( n->NbInverseElements( elemType ) == 0 )
10318 elemType = SMDSAbs_Face;
10319 if ( n->NbInverseElements( elemType ) == 0 )
10322 // unmark elements touching the fissure
10323 for ( ; nIt != theNodes.end(); ++nIt )
10324 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10326 // loop on elements touching the fissure to get their borders belonging to the fissure
10327 std::set< FissureBorder > fissureBorders;
10328 std::vector< const SMDS_MeshElement* > adjElems;
10329 std::vector< const SMDS_MeshNode* > nodes;
10330 SMDS_VolumeTool volTool;
10331 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10333 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10334 while ( invIt->more() )
10336 const SMDS_MeshElement* eInv = invIt->next();
10337 if ( eInv->isMarked() ) continue;
10338 eInv->setIsMarked( true );
10340 if ( elemType == SMDSAbs_Volume )
10342 volTool.Set( eInv );
10343 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10344 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10346 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10347 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10349 bool allOnFissure = true;
10350 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10351 if (( allOnFissure = theNodes.count( nn[ iN ])))
10352 nodes.push_back( nn[ iN ]);
10353 if ( allOnFissure )
10354 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10355 elemType, adjElems )));
10358 else // elemType == SMDSAbs_Face
10360 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10361 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10362 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10364 nn[1] = eInv->GetNode( iN );
10365 onFissure1 = theNodes.count( nn[1] );
10366 if ( onFissure0 && onFissure1 )
10367 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10369 onFissure0 = onFissure1;
10375 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10376 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10377 for ( ; bord != fissureBorders.end(); ++bord )
10379 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10382 } // findFissureBorders()
10384 //================================================================================
10386 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10387 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10388 * \param [in] theNodesNot - nodes not to duplicate
10389 * \param [out] theAffectedElems - the found elements
10391 //================================================================================
10393 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10394 TIDSortedElemSet& theAffectedElems)
10396 if ( theElemsOrNodes.empty() ) return;
10398 // find FissureBorder's
10400 std::vector< FissureBorder > fissure;
10401 std::vector< const SMDS_MeshElement* > elemsByFacet;
10403 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10404 if ( (*elIt)->GetType() == SMDSAbs_Node )
10406 findFissureBorders( theElemsOrNodes, fissure );
10410 fissure.reserve( theElemsOrNodes.size() );
10411 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10412 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10414 if ( fissure.empty() )
10417 // fill borderLinks
10419 TBorderLinks borderLinks;
10421 for ( size_t i = 0; i < fissure.size(); ++i )
10423 fissure[i].AddSelfTo( borderLinks );
10426 // get theAffectedElems
10428 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10429 for ( size_t i = 0; i < fissure.size(); ++i )
10430 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10432 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10433 false, /*markElem=*/true );
10436 std::vector<const SMDS_MeshNode *> facetNodes;
10437 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10438 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10440 // choose a side of fissure
10441 fissure[0].ChooseSide();
10442 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10444 size_t nbCheckedBorders = 0;
10445 while ( nbCheckedBorders < fissure.size() )
10447 // find a FissureBorder to treat
10448 FissureBorder* bord = 0;
10449 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10450 if ( fissure[i].GetMarkedElem() )
10451 bord = & fissure[i];
10452 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10453 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10455 bord = & fissure[i];
10456 bord->ChooseSide();
10457 theAffectedElems.insert( bord->GetMarkedElem() );
10459 if ( !bord ) return;
10460 ++nbCheckedBorders;
10462 // treat FissureBorder's linked to bord
10463 fissureNodes.clear();
10464 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10465 for ( size_t i = 0; i < bord->NbSub(); ++i )
10467 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10468 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10469 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10470 const SubBorder& sb = l2b->first;
10471 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10473 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10475 for ( int j = 0; j < sb._nbNodes; ++j )
10476 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10480 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10481 // until an elem adjacent to a neighbour FissureBorder is found
10482 facetNodes.clear();
10483 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10484 facetNodes.resize( sb._nbNodes + 1 );
10488 // check if bordElem is adjacent to a neighbour FissureBorder
10489 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10491 FissureBorder* bord2 = linkedBorders[j];
10492 if ( bord2 == bord ) continue;
10493 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10496 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10501 // find the next bordElem
10502 const SMDS_MeshElement* nextBordElem = 0;
10503 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10505 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10506 if ( fissureNodes.count( n )) continue;
10508 facetNodes[ sb._nbNodes ] = n;
10509 elemsByFacet.clear();
10510 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10512 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10513 if ( elemsByFacet[ iE ] != bordElem &&
10514 !elemsByFacet[ iE ]->isMarked() )
10516 theAffectedElems.insert( elemsByFacet[ iE ]);
10517 elemsByFacet[ iE ]->setIsMarked( true );
10518 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10519 nextBordElem = elemsByFacet[ iE ];
10523 bordElem = nextBordElem;
10525 } // while ( bordElem )
10527 linkedBorders.clear(); // not to treat this link any more
10529 } // loop on SubBorder's of a FissureBorder
10533 } // loop on FissureBorder's
10536 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10538 // mark nodes of theAffectedElems
10539 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10541 // unmark nodes of the fissure
10542 elIt = theElemsOrNodes.begin();
10543 if ( (*elIt)->GetType() == SMDSAbs_Node )
10544 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10546 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10548 std::vector< gp_XYZ > normVec;
10550 // loop on nodes of the fissure, add elements having marked nodes
10551 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10553 const SMDS_MeshElement* e = (*elIt);
10554 if ( e->GetType() != SMDSAbs_Node )
10555 e->setIsMarked( true ); // avoid adding a fissure element
10557 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10559 const SMDS_MeshNode* n = e->GetNode( iN );
10560 if ( fissEdgeNodes2Norm.count( n ))
10563 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10564 while ( invIt->more() )
10566 const SMDS_MeshElement* eInv = invIt->next();
10567 if ( eInv->isMarked() ) continue;
10568 eInv->setIsMarked( true );
10570 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10571 while( nIt->more() )
10572 if ( nIt->next()->isMarked())
10574 theAffectedElems.insert( eInv );
10575 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10576 n->setIsMarked( false );
10583 // add elements on the fissure edge
10584 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10585 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10587 const SMDS_MeshNode* edgeNode = n2N->first;
10588 const FissureNormal & normals = n2N->second;
10590 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10591 while ( invIt->more() )
10593 const SMDS_MeshElement* eInv = invIt->next();
10594 if ( eInv->isMarked() ) continue;
10595 eInv->setIsMarked( true );
10597 // classify eInv using normals
10598 bool toAdd = normals.IsIn( edgeNode, eInv );
10599 if ( toAdd ) // check if all nodes lie on the fissure edge
10601 bool notOnEdge = false;
10602 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10603 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10608 theAffectedElems.insert( eInv );
10614 } // findAffectedElems()
10617 //================================================================================
10619 * \brief Create elements equal (on same nodes) to given ones
10620 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10621 * elements of the uppest dimension are duplicated.
10623 //================================================================================
10625 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10627 ClearLastCreated();
10628 SMESHDS_Mesh* mesh = GetMeshDS();
10630 // get an element type and an iterator over elements
10632 SMDSAbs_ElementType type = SMDSAbs_All;
10633 SMDS_ElemIteratorPtr elemIt;
10634 if ( theElements.empty() )
10636 if ( mesh->NbNodes() == 0 )
10638 // get most complex type
10639 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10640 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10641 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10643 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10644 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10647 elemIt = mesh->elementsIterator( type );
10653 //type = (*theElements.begin())->GetType();
10654 elemIt = SMESHUtils::elemSetIterator( theElements );
10657 // un-mark all elements to avoid duplicating just created elements
10658 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10660 // duplicate elements
10662 ElemFeatures elemType;
10664 vector< const SMDS_MeshNode* > nodes;
10665 while ( elemIt->more() )
10667 const SMDS_MeshElement* elem = elemIt->next();
10668 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10669 ( elem->isMarked() ))
10672 elemType.Init( elem, /*basicOnly=*/false );
10673 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10675 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10676 newElem->setIsMarked( true );
10680 //================================================================================
10682 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10683 \param theElems - the list of elements (edges or faces) to be replicated
10684 The nodes for duplication could be found from these elements
10685 \param theNodesNot - list of nodes to NOT replicate
10686 \param theAffectedElems - the list of elements (cells and edges) to which the
10687 replicated nodes should be associated to.
10688 \return TRUE if operation has been completed successfully, FALSE otherwise
10690 //================================================================================
10692 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10693 const TIDSortedElemSet& theNodesNot,
10694 const TIDSortedElemSet& theAffectedElems )
10696 ClearLastCreated();
10698 if ( theElems.size() == 0 )
10701 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10706 TNodeNodeMap anOldNodeToNewNode;
10707 // duplicate elements and nodes
10708 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10709 // replce nodes by duplications
10710 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10714 //================================================================================
10716 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10717 \param theMeshDS - mesh instance
10718 \param theElems - the elements replicated or modified (nodes should be changed)
10719 \param theNodesNot - nodes to NOT replicate
10720 \param theNodeNodeMap - relation of old node to new created node
10721 \param theIsDoubleElem - flag os to replicate element or modify
10722 \return TRUE if operation has been completed successfully, FALSE otherwise
10724 //================================================================================
10726 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10727 const TIDSortedElemSet& theElems,
10728 const TIDSortedElemSet& theNodesNot,
10729 TNodeNodeMap& theNodeNodeMap,
10730 const bool theIsDoubleElem )
10732 // iterate through element and duplicate them (by nodes duplication)
10734 std::vector<const SMDS_MeshNode*> newNodes;
10735 ElemFeatures elemType;
10737 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10738 for ( ; elemItr != theElems.end(); ++elemItr )
10740 const SMDS_MeshElement* anElem = *elemItr;
10744 // duplicate nodes to duplicate element
10745 bool isDuplicate = false;
10746 newNodes.resize( anElem->NbNodes() );
10747 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10749 while ( anIter->more() )
10751 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10752 const SMDS_MeshNode* aNewNode = aCurrNode;
10753 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10754 if ( n2n != theNodeNodeMap.end() )
10756 aNewNode = n2n->second;
10758 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10761 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10762 copyPosition( aCurrNode, aNewNode );
10763 theNodeNodeMap[ aCurrNode ] = aNewNode;
10764 myLastCreatedNodes.push_back( aNewNode );
10766 isDuplicate |= (aCurrNode != aNewNode);
10767 newNodes[ ind++ ] = aNewNode;
10769 if ( !isDuplicate )
10772 if ( theIsDoubleElem )
10773 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10775 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10782 //================================================================================
10784 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10785 \param theNodes - identifiers of nodes to be doubled
10786 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10787 nodes. If list of element identifiers is empty then nodes are doubled but
10788 they not assigned to elements
10789 \return TRUE if operation has been completed successfully, FALSE otherwise
10791 //================================================================================
10793 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10794 const std::list< int >& theListOfModifiedElems )
10796 ClearLastCreated();
10798 if ( theListOfNodes.size() == 0 )
10801 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10805 // iterate through nodes and duplicate them
10807 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10809 std::list< int >::const_iterator aNodeIter;
10810 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10812 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10818 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10821 copyPosition( aNode, aNewNode );
10822 anOldNodeToNewNode[ aNode ] = aNewNode;
10823 myLastCreatedNodes.push_back( aNewNode );
10827 // Change nodes of elements
10829 std::vector<const SMDS_MeshNode*> aNodeArr;
10831 std::list< int >::const_iterator anElemIter;
10832 for ( anElemIter = theListOfModifiedElems.begin();
10833 anElemIter != theListOfModifiedElems.end();
10836 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10840 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10841 for( size_t i = 0; i < aNodeArr.size(); ++i )
10843 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10844 anOldNodeToNewNode.find( aNodeArr[ i ]);
10845 if ( n2n != anOldNodeToNewNode.end() )
10846 aNodeArr[ i ] = n2n->second;
10848 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10856 //================================================================================
10858 \brief Check if element located inside shape
10859 \return TRUE if IN or ON shape, FALSE otherwise
10861 //================================================================================
10863 template<class Classifier>
10864 bool isInside(const SMDS_MeshElement* theElem,
10865 Classifier& theClassifier,
10866 const double theTol)
10868 gp_XYZ centerXYZ (0, 0, 0);
10869 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10870 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10872 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10873 theClassifier.Perform(aPnt, theTol);
10874 TopAbs_State aState = theClassifier.State();
10875 return (aState == TopAbs_IN || aState == TopAbs_ON );
10878 //================================================================================
10880 * \brief Classifier of the 3D point on the TopoDS_Face
10881 * with interaface suitable for isInside()
10883 //================================================================================
10885 struct _FaceClassifier
10887 Extrema_ExtPS _extremum;
10888 BRepAdaptor_Surface _surface;
10889 TopAbs_State _state;
10891 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10893 _extremum.Initialize( _surface,
10894 _surface.FirstUParameter(), _surface.LastUParameter(),
10895 _surface.FirstVParameter(), _surface.LastVParameter(),
10896 _surface.Tolerance(), _surface.Tolerance() );
10898 void Perform(const gp_Pnt& aPnt, double theTol)
10901 _state = TopAbs_OUT;
10902 _extremum.Perform(aPnt);
10903 if ( _extremum.IsDone() )
10904 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10905 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10907 TopAbs_State State() const
10914 //================================================================================
10916 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10917 This method is the first step of DoubleNodeElemGroupsInRegion.
10918 \param theElems - list of groups of elements (edges or faces) to be replicated
10919 \param theNodesNot - list of groups of nodes not to replicated
10920 \param theShape - shape to detect affected elements (element which geometric center
10921 located on or inside shape). If the shape is null, detection is done on faces orientations
10922 (select elements with a gravity center on the side given by faces normals).
10923 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10924 The replicated nodes should be associated to affected elements.
10926 \sa DoubleNodeElemGroupsInRegion()
10928 //================================================================================
10930 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10931 const TIDSortedElemSet& theNodesNot,
10932 const TopoDS_Shape& theShape,
10933 TIDSortedElemSet& theAffectedElems)
10935 if ( theShape.IsNull() )
10937 findAffectedElems( theElems, theAffectedElems );
10941 const double aTol = Precision::Confusion();
10942 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10943 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10944 if ( theShape.ShapeType() == TopAbs_SOLID )
10946 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10947 bsc3d->PerformInfinitePoint(aTol);
10949 else if (theShape.ShapeType() == TopAbs_FACE )
10951 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10954 // iterates on indicated elements and get elements by back references from their nodes
10955 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10956 for ( ; elemItr != theElems.end(); ++elemItr )
10958 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10959 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10960 while ( nodeItr->more() )
10962 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10963 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10965 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10966 while ( backElemItr->more() )
10968 const SMDS_MeshElement* curElem = backElemItr->next();
10969 if ( curElem && theElems.find(curElem) == theElems.end() &&
10971 isInside( curElem, *bsc3d, aTol ) :
10972 isInside( curElem, *aFaceClassifier, aTol )))
10973 theAffectedElems.insert( curElem );
10981 //================================================================================
10983 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10984 \param theElems - group of of elements (edges or faces) to be replicated
10985 \param theNodesNot - group of nodes not to replicate
10986 \param theShape - shape to detect affected elements (element which geometric center
10987 located on or inside shape).
10988 The replicated nodes should be associated to affected elements.
10989 \return TRUE if operation has been completed successfully, FALSE otherwise
10991 //================================================================================
10993 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10994 const TIDSortedElemSet& theNodesNot,
10995 const TopoDS_Shape& theShape )
10997 if ( theShape.IsNull() )
11000 const double aTol = Precision::Confusion();
11001 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11002 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11003 if ( theShape.ShapeType() == TopAbs_SOLID )
11005 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11006 bsc3d->PerformInfinitePoint(aTol);
11008 else if (theShape.ShapeType() == TopAbs_FACE )
11010 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11013 // iterates on indicated elements and get elements by back references from their nodes
11014 TIDSortedElemSet anAffected;
11015 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11016 for ( ; elemItr != theElems.end(); ++elemItr )
11018 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11022 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11023 while ( nodeItr->more() )
11025 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11026 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11028 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11029 while ( backElemItr->more() )
11031 const SMDS_MeshElement* curElem = backElemItr->next();
11032 if ( curElem && theElems.find(curElem) == theElems.end() &&
11034 isInside( curElem, *bsc3d, aTol ) :
11035 isInside( curElem, *aFaceClassifier, aTol )))
11036 anAffected.insert( curElem );
11040 return DoubleNodes( theElems, theNodesNot, anAffected );
11044 * \brief compute an oriented angle between two planes defined by four points.
11045 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11046 * @param p0 base of the rotation axe
11047 * @param p1 extremity of the rotation axe
11048 * @param g1 belongs to the first plane
11049 * @param g2 belongs to the second plane
11051 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11053 gp_Vec vref(p0, p1);
11056 gp_Vec n1 = vref.Crossed(v1);
11057 gp_Vec n2 = vref.Crossed(v2);
11059 return n2.AngleWithRef(n1, vref);
11061 catch ( Standard_Failure& ) {
11063 return Max( v1.Magnitude(), v2.Magnitude() );
11067 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11068 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11069 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11070 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11071 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11072 * 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.
11073 * 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.
11074 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11075 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11076 * \param theElems - list of groups of volumes, where a group of volume is a set of
11077 * SMDS_MeshElements sorted by Id.
11078 * \param createJointElems - if TRUE, create the elements
11079 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11080 * the boundary between \a theDomains and the rest mesh
11081 * \return TRUE if operation has been completed successfully, FALSE otherwise
11083 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11084 bool createJointElems,
11085 bool onAllBoundaries)
11087 // MESSAGE("----------------------------------------------");
11088 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11089 // MESSAGE("----------------------------------------------");
11091 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11092 meshDS->BuildDownWardConnectivity(true);
11094 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11096 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11097 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11098 // build the list of nodes shared by 2 or more domains, with their domain indexes
11100 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11101 std::map<int,int>celldom; // cell vtkId --> domain
11102 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11103 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11104 faceDomains.clear();
11106 cellDomains.clear();
11107 nodeDomains.clear();
11108 std::map<int,int> emptyMap;
11109 std::set<int> emptySet;
11112 //MESSAGE(".. Number of domains :"<<theElems.size());
11114 TIDSortedElemSet theRestDomElems;
11115 const int iRestDom = -1;
11116 const int idom0 = onAllBoundaries ? iRestDom : 0;
11117 const int nbDomains = theElems.size();
11119 // Check if the domains do not share an element
11120 for (int idom = 0; idom < nbDomains-1; idom++)
11122 // MESSAGE("... Check of domain #" << idom);
11123 const TIDSortedElemSet& domain = theElems[idom];
11124 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11125 for (; elemItr != domain.end(); ++elemItr)
11127 const SMDS_MeshElement* anElem = *elemItr;
11128 int idombisdeb = idom + 1 ;
11129 // check if the element belongs to a domain further in the list
11130 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11132 const TIDSortedElemSet& domainbis = theElems[idombis];
11133 if ( domainbis.count( anElem ))
11135 MESSAGE(".... Domain #" << idom);
11136 MESSAGE(".... Domain #" << idombis);
11137 throw SALOME_Exception("The domains are not disjoint.");
11144 for (int idom = 0; idom < nbDomains; idom++)
11147 // --- build a map (face to duplicate --> volume to modify)
11148 // with all the faces shared by 2 domains (group of elements)
11149 // and corresponding volume of this domain, for each shared face.
11150 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11152 //MESSAGE("... Neighbors of domain #" << idom);
11153 const TIDSortedElemSet& domain = theElems[idom];
11154 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11155 for (; elemItr != domain.end(); ++elemItr)
11157 const SMDS_MeshElement* anElem = *elemItr;
11160 int vtkId = anElem->GetVtkID();
11161 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11162 int neighborsVtkIds[NBMAXNEIGHBORS];
11163 int downIds[NBMAXNEIGHBORS];
11164 unsigned char downTypes[NBMAXNEIGHBORS];
11165 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11166 for (int n = 0; n < nbNeighbors; n++)
11168 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11169 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11170 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11173 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11175 // MESSAGE("Domain " << idombis);
11176 const TIDSortedElemSet& domainbis = theElems[idombis];
11177 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11179 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11181 DownIdType face(downIds[n], downTypes[n]);
11182 if (!faceDomains[face].count(idom))
11184 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11185 celldom[vtkId] = idom;
11186 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11190 theRestDomElems.insert( elem );
11191 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11192 celldom[neighborsVtkIds[n]] = iRestDom;
11200 //MESSAGE("Number of shared faces " << faceDomains.size());
11201 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11203 // --- explore the shared faces domain by domain,
11204 // explore the nodes of the face and see if they belong to a cell in the domain,
11205 // which has only a node or an edge on the border (not a shared face)
11207 for (int idomain = idom0; idomain < nbDomains; idomain++)
11209 //MESSAGE("Domain " << idomain);
11210 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11211 itface = faceDomains.begin();
11212 for (; itface != faceDomains.end(); ++itface)
11214 const std::map<int, int>& domvol = itface->second;
11215 if (!domvol.count(idomain))
11217 DownIdType face = itface->first;
11218 //MESSAGE(" --- face " << face.cellId);
11219 std::set<int> oldNodes;
11221 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11222 std::set<int>::iterator itn = oldNodes.begin();
11223 for (; itn != oldNodes.end(); ++itn)
11226 //MESSAGE(" node " << oldId);
11227 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11228 for (int i=0; i<l.ncells; i++)
11230 int vtkId = l.cells[i];
11231 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11232 if (!domain.count(anElem))
11234 int vtkType = grid->GetCellType(vtkId);
11235 int downId = grid->CellIdToDownId(vtkId);
11238 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11239 continue; // not OK at this stage of the algorithm:
11240 //no cells created after BuildDownWardConnectivity
11242 DownIdType aCell(downId, vtkType);
11243 cellDomains[aCell][idomain] = vtkId;
11244 celldom[vtkId] = idomain;
11245 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11251 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11252 // for each shared face, get the nodes
11253 // for each node, for each domain of the face, create a clone of the node
11255 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11256 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11257 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11259 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11260 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11261 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11263 //MESSAGE(".. Duplication of the nodes");
11264 for (int idomain = idom0; idomain < nbDomains; idomain++)
11266 itface = faceDomains.begin();
11267 for (; itface != faceDomains.end(); ++itface)
11269 const std::map<int, int>& domvol = itface->second;
11270 if (!domvol.count(idomain))
11272 DownIdType face = itface->first;
11273 //MESSAGE(" --- face " << face.cellId);
11274 std::set<int> oldNodes;
11276 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11277 std::set<int>::iterator itn = oldNodes.begin();
11278 for (; itn != oldNodes.end(); ++itn)
11281 if (nodeDomains[oldId].empty())
11283 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11284 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11286 std::map<int, int>::const_iterator itdom = domvol.begin();
11287 for (; itdom != domvol.end(); ++itdom)
11289 int idom = itdom->first;
11290 //MESSAGE(" domain " << idom);
11291 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11293 if (nodeDomains[oldId].size() >= 2) // a multiple node
11295 vector<int> orderedDoms;
11296 //MESSAGE("multiple node " << oldId);
11297 if (mutipleNodes.count(oldId))
11298 orderedDoms = mutipleNodes[oldId];
11301 map<int,int>::iterator it = nodeDomains[oldId].begin();
11302 for (; it != nodeDomains[oldId].end(); ++it)
11303 orderedDoms.push_back(it->first);
11305 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11306 //stringstream txt;
11307 //for (int i=0; i<orderedDoms.size(); i++)
11308 // txt << orderedDoms[i] << " ";
11309 //MESSAGE("orderedDoms " << txt.str());
11310 mutipleNodes[oldId] = orderedDoms;
11312 double *coords = grid->GetPoint(oldId);
11313 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11314 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11315 int newId = newNode->GetVtkID();
11316 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11317 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11324 //MESSAGE(".. Creation of elements");
11325 for (int idomain = idom0; idomain < nbDomains; idomain++)
11327 itface = faceDomains.begin();
11328 for (; itface != faceDomains.end(); ++itface)
11330 std::map<int, int> domvol = itface->second;
11331 if (!domvol.count(idomain))
11333 DownIdType face = itface->first;
11334 //MESSAGE(" --- face " << face.cellId);
11335 std::set<int> oldNodes;
11337 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11338 int nbMultipleNodes = 0;
11339 std::set<int>::iterator itn = oldNodes.begin();
11340 for (; itn != oldNodes.end(); ++itn)
11343 if (mutipleNodes.count(oldId))
11346 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11348 //MESSAGE("multiple Nodes detected on a shared face");
11349 int downId = itface->first.cellId;
11350 unsigned char cellType = itface->first.cellType;
11351 // --- shared edge or shared face ?
11352 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11355 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11356 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11357 if (mutipleNodes.count(nodes[i]))
11358 if (!mutipleNodesToFace.count(nodes[i]))
11359 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11361 else // shared face (between two volumes)
11363 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11364 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11365 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11366 for (int ie =0; ie < nbEdges; ie++)
11369 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11370 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11372 vector<int> vn0 = mutipleNodes[nodes[0]];
11373 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11375 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11376 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11377 if ( vn0[i0] == vn1[i1] )
11378 doms.push_back( vn0[ i0 ]);
11379 if ( doms.size() > 2 )
11381 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11382 double *coords = grid->GetPoint(nodes[0]);
11383 gp_Pnt p0(coords[0], coords[1], coords[2]);
11384 coords = grid->GetPoint(nodes[nbNodes - 1]);
11385 gp_Pnt p1(coords[0], coords[1], coords[2]);
11387 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11388 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11389 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11390 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11391 for ( size_t id = 0; id < doms.size(); id++ )
11393 int idom = doms[id];
11394 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11395 for ( int ivol = 0; ivol < nbvol; ivol++ )
11397 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11398 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11399 if (domain.count(elem))
11401 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11402 domvol[idom] = (SMDS_MeshVolume*) svol;
11403 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11404 double values[3] = { 0,0,0 };
11405 vtkIdType npts = 0;
11406 vtkIdType const *pts(nullptr);
11407 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11408 for ( vtkIdType i = 0; i < npts; ++i )
11410 double *coords = grid->GetPoint( pts[i] );
11411 for ( int j = 0; j < 3; ++j )
11412 values[j] += coords[j] / npts;
11416 gref.SetCoord( values[0], values[1], values[2] );
11417 angleDom[idom] = 0;
11421 gp_Pnt g( values[0], values[1], values[2] );
11422 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11423 //MESSAGE(" angle=" << angleDom[idom]);
11429 map<double, int> sortedDom; // sort domains by angle
11430 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11431 sortedDom[ia->second] = ia->first;
11432 vector<int> vnodes;
11434 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11436 vdom.push_back(ib->second);
11437 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11439 for (int ino = 0; ino < nbNodes; ino++)
11440 vnodes.push_back(nodes[ino]);
11441 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11450 // --- iterate on shared faces (volumes to modify, face to extrude)
11451 // get node id's of the face (id SMDS = id VTK)
11452 // create flat element with old and new nodes if requested
11454 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11455 // (domain1 X domain2) = domain1 + MAXINT*domain2
11457 std::map<int, std::map<long,int> > nodeQuadDomains;
11458 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11460 //MESSAGE(".. Creation of elements: simple junction");
11461 if (createJointElems)
11463 string joints2DName = "joints2D";
11464 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11465 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11466 string joints3DName = "joints3D";
11467 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11468 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11470 itface = faceDomains.begin();
11471 for (; itface != faceDomains.end(); ++itface)
11473 DownIdType face = itface->first;
11474 std::set<int> oldNodes;
11475 std::set<int>::iterator itn;
11477 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11479 std::map<int, int> domvol = itface->second;
11480 std::map<int, int>::iterator itdom = domvol.begin();
11481 int dom1 = itdom->first;
11482 int vtkVolId = itdom->second;
11484 int dom2 = itdom->first;
11485 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11487 stringstream grpname;
11490 grpname << dom1 << "_" << dom2;
11492 grpname << dom2 << "_" << dom1;
11493 string namegrp = grpname.str();
11494 if (!mapOfJunctionGroups.count(namegrp))
11495 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11496 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11498 sgrp->Add(vol->GetID());
11499 if (vol->GetType() == SMDSAbs_Volume)
11500 joints3DGrp->Add(vol->GetID());
11501 else if (vol->GetType() == SMDSAbs_Face)
11502 joints2DGrp->Add(vol->GetID());
11506 // --- create volumes on multiple domain intersection if requested
11507 // iterate on mutipleNodesToFace
11508 // iterate on edgesMultiDomains
11510 //MESSAGE(".. Creation of elements: multiple junction");
11511 if (createJointElems)
11513 // --- iterate on mutipleNodesToFace
11515 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11516 for (; itn != mutipleNodesToFace.end(); ++itn)
11518 int node = itn->first;
11519 vector<int> orderDom = itn->second;
11520 vector<vtkIdType> orderedNodes;
11521 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11522 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11523 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11525 stringstream grpname;
11527 grpname << 0 << "_" << 0;
11528 string namegrp = grpname.str();
11529 if (!mapOfJunctionGroups.count(namegrp))
11530 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11531 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11533 sgrp->Add(face->GetID());
11536 // --- iterate on edgesMultiDomains
11538 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11539 for (; ite != edgesMultiDomains.end(); ++ite)
11541 vector<int> nodes = ite->first;
11542 vector<int> orderDom = ite->second;
11543 vector<vtkIdType> orderedNodes;
11544 if (nodes.size() == 2)
11546 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11547 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11548 if ( orderDom.size() == 3 )
11549 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11550 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11552 for (int idom = orderDom.size()-1; idom >=0; idom--)
11553 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11554 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11556 string namegrp = "jointsMultiples";
11557 if (!mapOfJunctionGroups.count(namegrp))
11558 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11559 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11561 sgrp->Add(vol->GetID());
11565 //INFOS("Quadratic multiple joints not implemented");
11566 // TODO quadratic nodes
11571 // --- list the explicit faces and edges of the mesh that need to be modified,
11572 // i.e. faces and edges built with one or more duplicated nodes.
11573 // associate these faces or edges to their corresponding domain.
11574 // only the first domain found is kept when a face or edge is shared
11576 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11577 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11578 faceOrEdgeDom.clear();
11581 //MESSAGE(".. Modification of elements");
11582 for (int idomain = idom0; idomain < nbDomains; idomain++)
11584 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11585 for (; itnod != nodeDomains.end(); ++itnod)
11587 int oldId = itnod->first;
11588 //MESSAGE(" node " << oldId);
11589 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11590 for (int i = 0; i < l.ncells; i++)
11592 int vtkId = l.cells[i];
11593 int vtkType = grid->GetCellType(vtkId);
11594 int downId = grid->CellIdToDownId(vtkId);
11596 continue; // new cells: not to be modified
11597 DownIdType aCell(downId, vtkType);
11598 int volParents[1000];
11599 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11600 for (int j = 0; j < nbvol; j++)
11601 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11602 if (!feDom.count(vtkId))
11604 feDom[vtkId] = idomain;
11605 faceOrEdgeDom[aCell] = emptyMap;
11606 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11607 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11608 // << " type " << vtkType << " downId " << downId);
11614 // --- iterate on shared faces (volumes to modify, face to extrude)
11615 // get node id's of the face
11616 // replace old nodes by new nodes in volumes, and update inverse connectivity
11618 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11619 for (int m=0; m<3; m++)
11621 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11622 itface = (*amap).begin();
11623 for (; itface != (*amap).end(); ++itface)
11625 DownIdType face = itface->first;
11626 std::set<int> oldNodes;
11627 std::set<int>::iterator itn;
11629 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11630 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11631 std::map<int, int> localClonedNodeIds;
11633 std::map<int, int> domvol = itface->second;
11634 std::map<int, int>::iterator itdom = domvol.begin();
11635 for (; itdom != domvol.end(); ++itdom)
11637 int idom = itdom->first;
11638 int vtkVolId = itdom->second;
11639 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11640 localClonedNodeIds.clear();
11641 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11644 if (nodeDomains[oldId].count(idom))
11646 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11647 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11650 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11655 // Remove empty groups (issue 0022812)
11656 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11657 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11659 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11660 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11663 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11664 grid->DeleteLinks();
11672 * \brief Double nodes on some external faces and create flat elements.
11673 * Flat elements are mainly used by some types of mechanic calculations.
11675 * Each group of the list must be constituted of faces.
11676 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11677 * @param theElems - list of groups of faces, where a group of faces is a set of
11678 * SMDS_MeshElements sorted by Id.
11679 * @return TRUE if operation has been completed successfully, FALSE otherwise
11681 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11683 // MESSAGE("-------------------------------------------------");
11684 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11685 // MESSAGE("-------------------------------------------------");
11687 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11689 // --- For each group of faces
11690 // duplicate the nodes, create a flat element based on the face
11691 // replace the nodes of the faces by their clones
11693 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11694 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11695 clonedNodes.clear();
11696 intermediateNodes.clear();
11697 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11698 mapOfJunctionGroups.clear();
11700 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11702 const TIDSortedElemSet& domain = theElems[idom];
11703 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11704 for ( ; elemItr != domain.end(); ++elemItr )
11706 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11709 // MESSAGE("aFace=" << aFace->GetID());
11710 bool isQuad = aFace->IsQuadratic();
11711 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11713 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11715 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11716 while (nodeIt->more())
11718 const SMDS_MeshNode* node = nodeIt->next();
11719 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11721 ln2.push_back(node);
11723 ln0.push_back(node);
11725 const SMDS_MeshNode* clone = 0;
11726 if (!clonedNodes.count(node))
11728 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11729 copyPosition( node, clone );
11730 clonedNodes[node] = clone;
11733 clone = clonedNodes[node];
11736 ln3.push_back(clone);
11738 ln1.push_back(clone);
11740 const SMDS_MeshNode* inter = 0;
11741 if (isQuad && (!isMedium))
11743 if (!intermediateNodes.count(node))
11745 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11746 copyPosition( node, inter );
11747 intermediateNodes[node] = inter;
11750 inter = intermediateNodes[node];
11751 ln4.push_back(inter);
11755 // --- extrude the face
11757 vector<const SMDS_MeshNode*> ln;
11758 SMDS_MeshVolume* vol = 0;
11759 vtkIdType aType = aFace->GetVtkType();
11763 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11764 // MESSAGE("vol prism " << vol->GetID());
11765 ln.push_back(ln1[0]);
11766 ln.push_back(ln1[1]);
11767 ln.push_back(ln1[2]);
11770 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11771 // MESSAGE("vol hexa " << vol->GetID());
11772 ln.push_back(ln1[0]);
11773 ln.push_back(ln1[1]);
11774 ln.push_back(ln1[2]);
11775 ln.push_back(ln1[3]);
11777 case VTK_QUADRATIC_TRIANGLE:
11778 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11779 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11780 // MESSAGE("vol quad prism " << vol->GetID());
11781 ln.push_back(ln1[0]);
11782 ln.push_back(ln1[1]);
11783 ln.push_back(ln1[2]);
11784 ln.push_back(ln3[0]);
11785 ln.push_back(ln3[1]);
11786 ln.push_back(ln3[2]);
11788 case VTK_QUADRATIC_QUAD:
11789 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11790 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11791 // ln4[0], ln4[1], ln4[2], ln4[3]);
11792 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11793 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11794 ln4[0], ln4[1], ln4[2], ln4[3]);
11795 // MESSAGE("vol quad hexa " << vol->GetID());
11796 ln.push_back(ln1[0]);
11797 ln.push_back(ln1[1]);
11798 ln.push_back(ln1[2]);
11799 ln.push_back(ln1[3]);
11800 ln.push_back(ln3[0]);
11801 ln.push_back(ln3[1]);
11802 ln.push_back(ln3[2]);
11803 ln.push_back(ln3[3]);
11813 stringstream grpname;
11816 string namegrp = grpname.str();
11817 if (!mapOfJunctionGroups.count(namegrp))
11818 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11819 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11821 sgrp->Add(vol->GetID());
11824 // --- modify the face
11826 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11833 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11834 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11835 * groups of faces to remove inside the object, (idem edges).
11836 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11838 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11839 const TopoDS_Shape& theShape,
11840 SMESH_NodeSearcher* theNodeSearcher,
11841 const char* groupName,
11842 std::vector<double>& nodesCoords,
11843 std::vector<std::vector<int> >& listOfListOfNodes)
11845 // MESSAGE("--------------------------------");
11846 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11847 // MESSAGE("--------------------------------");
11849 // --- zone of volumes to remove is given :
11850 // 1 either by a geom shape (one or more vertices) and a radius,
11851 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11852 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11853 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11854 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11855 // defined by it's name.
11857 SMESHDS_GroupBase* groupDS = 0;
11858 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11859 while ( groupIt->more() )
11862 SMESH_Group * group = groupIt->next();
11863 if ( !group ) continue;
11864 groupDS = group->GetGroupDS();
11865 if ( !groupDS || groupDS->IsEmpty() ) continue;
11866 std::string grpName = group->GetName();
11867 //MESSAGE("grpName=" << grpName);
11868 if (grpName == groupName)
11874 bool isNodeGroup = false;
11875 bool isNodeCoords = false;
11878 if (groupDS->GetType() != SMDSAbs_Node)
11880 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11883 if (nodesCoords.size() > 0)
11884 isNodeCoords = true; // a list o nodes given by their coordinates
11885 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11887 // --- define groups to build
11889 // --- group of SMDS volumes
11890 string grpvName = groupName;
11891 grpvName += "_vol";
11892 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11895 MESSAGE("group not created " << grpvName);
11898 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11900 // --- group of SMDS faces on the skin
11901 string grpsName = groupName;
11902 grpsName += "_skin";
11903 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11906 MESSAGE("group not created " << grpsName);
11909 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11911 // --- group of SMDS faces internal (several shapes)
11912 string grpiName = groupName;
11913 grpiName += "_internalFaces";
11914 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11917 MESSAGE("group not created " << grpiName);
11920 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11922 // --- group of SMDS faces internal (several shapes)
11923 string grpeiName = groupName;
11924 grpeiName += "_internalEdges";
11925 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11928 MESSAGE("group not created " << grpeiName);
11931 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11933 // --- build downward connectivity
11935 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11936 meshDS->BuildDownWardConnectivity(true);
11937 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11939 // --- set of volumes detected inside
11941 std::set<int> setOfInsideVol;
11942 std::set<int> setOfVolToCheck;
11944 std::vector<gp_Pnt> gpnts;
11947 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11949 //MESSAGE("group of nodes provided");
11950 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11951 while ( elemIt->more() )
11953 const SMDS_MeshElement* elem = elemIt->next();
11956 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11959 SMDS_MeshElement* vol = 0;
11960 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11961 while (volItr->more())
11963 vol = (SMDS_MeshElement*)volItr->next();
11964 setOfInsideVol.insert(vol->GetVtkID());
11965 sgrp->Add(vol->GetID());
11969 else if (isNodeCoords)
11971 //MESSAGE("list of nodes coordinates provided");
11974 while ( i < nodesCoords.size()-2 )
11976 double x = nodesCoords[i++];
11977 double y = nodesCoords[i++];
11978 double z = nodesCoords[i++];
11979 gp_Pnt p = gp_Pnt(x, y ,z);
11980 gpnts.push_back(p);
11981 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11985 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11987 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11988 TopTools_IndexedMapOfShape vertexMap;
11989 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11990 gp_Pnt p = gp_Pnt(0,0,0);
11991 if (vertexMap.Extent() < 1)
11994 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11996 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11997 p = BRep_Tool::Pnt(vertex);
11998 gpnts.push_back(p);
11999 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12003 if (gpnts.size() > 0)
12005 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12006 //MESSAGE("startNode->nodeId " << nodeId);
12008 double radius2 = radius*radius;
12009 //MESSAGE("radius2 " << radius2);
12011 // --- volumes on start node
12013 setOfVolToCheck.clear();
12014 SMDS_MeshElement* startVol = 0;
12015 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12016 while (volItr->more())
12018 startVol = (SMDS_MeshElement*)volItr->next();
12019 setOfVolToCheck.insert(startVol->GetVtkID());
12021 if (setOfVolToCheck.empty())
12023 MESSAGE("No volumes found");
12027 // --- starting with central volumes then their neighbors, check if they are inside
12028 // or outside the domain, until no more new neighbor volume is inside.
12029 // Fill the group of inside volumes
12031 std::map<int, double> mapOfNodeDistance2;
12032 mapOfNodeDistance2.clear();
12033 std::set<int> setOfOutsideVol;
12034 while (!setOfVolToCheck.empty())
12036 std::set<int>::iterator it = setOfVolToCheck.begin();
12038 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12039 bool volInside = false;
12040 vtkIdType npts = 0;
12041 vtkIdType const *pts(nullptr);
12042 grid->GetCellPoints(vtkId, npts, pts);
12043 for (int i=0; i<npts; i++)
12045 double distance2 = 0;
12046 if (mapOfNodeDistance2.count(pts[i]))
12048 distance2 = mapOfNodeDistance2[pts[i]];
12049 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12053 double *coords = grid->GetPoint(pts[i]);
12054 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12056 for ( size_t j = 0; j < gpnts.size(); j++ )
12058 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12059 if (d2 < distance2)
12062 if (distance2 < radius2)
12066 mapOfNodeDistance2[pts[i]] = distance2;
12067 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12069 if (distance2 < radius2)
12071 volInside = true; // one or more nodes inside the domain
12072 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12078 setOfInsideVol.insert(vtkId);
12079 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12080 int neighborsVtkIds[NBMAXNEIGHBORS];
12081 int downIds[NBMAXNEIGHBORS];
12082 unsigned char downTypes[NBMAXNEIGHBORS];
12083 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12084 for (int n = 0; n < nbNeighbors; n++)
12085 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12086 setOfVolToCheck.insert(neighborsVtkIds[n]);
12090 setOfOutsideVol.insert(vtkId);
12091 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12093 setOfVolToCheck.erase(vtkId);
12097 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12098 // If yes, add the volume to the inside set
12100 bool addedInside = true;
12101 std::set<int> setOfVolToReCheck;
12102 while (addedInside)
12104 //MESSAGE(" --------------------------- re check");
12105 addedInside = false;
12106 std::set<int>::iterator itv = setOfInsideVol.begin();
12107 for (; itv != setOfInsideVol.end(); ++itv)
12110 int neighborsVtkIds[NBMAXNEIGHBORS];
12111 int downIds[NBMAXNEIGHBORS];
12112 unsigned char downTypes[NBMAXNEIGHBORS];
12113 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12114 for (int n = 0; n < nbNeighbors; n++)
12115 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12116 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12118 setOfVolToCheck = setOfVolToReCheck;
12119 setOfVolToReCheck.clear();
12120 while (!setOfVolToCheck.empty())
12122 std::set<int>::iterator it = setOfVolToCheck.begin();
12124 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12126 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12127 int countInside = 0;
12128 int neighborsVtkIds[NBMAXNEIGHBORS];
12129 int downIds[NBMAXNEIGHBORS];
12130 unsigned char downTypes[NBMAXNEIGHBORS];
12131 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12132 for (int n = 0; n < nbNeighbors; n++)
12133 if (setOfInsideVol.count(neighborsVtkIds[n]))
12135 //MESSAGE("countInside " << countInside);
12136 if (countInside > 1)
12138 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12139 setOfInsideVol.insert(vtkId);
12140 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12141 addedInside = true;
12144 setOfVolToReCheck.insert(vtkId);
12146 setOfVolToCheck.erase(vtkId);
12150 // --- map of Downward faces at the boundary, inside the global volume
12151 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12152 // fill group of SMDS faces inside the volume (when several volume shapes)
12153 // fill group of SMDS faces on the skin of the global volume (if skin)
12155 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12156 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12157 std::set<int>::iterator it = setOfInsideVol.begin();
12158 for (; it != setOfInsideVol.end(); ++it)
12161 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12162 int neighborsVtkIds[NBMAXNEIGHBORS];
12163 int downIds[NBMAXNEIGHBORS];
12164 unsigned char downTypes[NBMAXNEIGHBORS];
12165 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12166 for (int n = 0; n < nbNeighbors; n++)
12168 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12169 if (neighborDim == 3)
12171 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12173 DownIdType face(downIds[n], downTypes[n]);
12174 boundaryFaces[face] = vtkId;
12176 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12177 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12178 if (vtkFaceId >= 0)
12180 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12181 // find also the smds edges on this face
12182 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12183 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12184 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12185 for (int i = 0; i < nbEdges; i++)
12187 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12188 if (vtkEdgeId >= 0)
12189 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12193 else if (neighborDim == 2) // skin of the volume
12195 DownIdType face(downIds[n], downTypes[n]);
12196 skinFaces[face] = vtkId;
12197 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12198 if (vtkFaceId >= 0)
12199 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12204 // --- identify the edges constituting the wire of each subshape on the skin
12205 // define polylines with the nodes of edges, equivalent to wires
12206 // project polylines on subshapes, and partition, to get geom faces
12208 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12209 std::set<int> emptySet;
12211 std::set<int> shapeIds;
12213 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12214 while (itelem->more())
12216 const SMDS_MeshElement *elem = itelem->next();
12217 int shapeId = elem->getshapeId();
12218 int vtkId = elem->GetVtkID();
12219 if (!shapeIdToVtkIdSet.count(shapeId))
12221 shapeIdToVtkIdSet[shapeId] = emptySet;
12222 shapeIds.insert(shapeId);
12224 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12227 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12228 std::set<DownIdType, DownIdCompare> emptyEdges;
12229 emptyEdges.clear();
12231 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12232 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12234 int shapeId = itShape->first;
12235 //MESSAGE(" --- Shape ID --- "<< shapeId);
12236 shapeIdToEdges[shapeId] = emptyEdges;
12238 std::vector<int> nodesEdges;
12240 std::set<int>::iterator its = itShape->second.begin();
12241 for (; its != itShape->second.end(); ++its)
12244 //MESSAGE(" " << vtkId);
12245 int neighborsVtkIds[NBMAXNEIGHBORS];
12246 int downIds[NBMAXNEIGHBORS];
12247 unsigned char downTypes[NBMAXNEIGHBORS];
12248 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12249 for (int n = 0; n < nbNeighbors; n++)
12251 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12253 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12254 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12255 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12257 DownIdType edge(downIds[n], downTypes[n]);
12258 if (!shapeIdToEdges[shapeId].count(edge))
12260 shapeIdToEdges[shapeId].insert(edge);
12262 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12263 nodesEdges.push_back(vtkNodeId[0]);
12264 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12265 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12271 std::list<int> order;
12273 if (nodesEdges.size() > 0)
12275 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12276 nodesEdges[0] = -1;
12277 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12278 nodesEdges[1] = -1; // do not reuse this edge
12282 int nodeTofind = order.back(); // try first to push back
12284 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12285 if (nodesEdges[i] == nodeTofind)
12287 if ( i == (int) nodesEdges.size() )
12288 found = false; // no follower found on back
12291 if (i%2) // odd ==> use the previous one
12292 if (nodesEdges[i-1] < 0)
12296 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12297 nodesEdges[i-1] = -1;
12299 else // even ==> use the next one
12300 if (nodesEdges[i+1] < 0)
12304 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12305 nodesEdges[i+1] = -1;
12310 // try to push front
12312 nodeTofind = order.front(); // try to push front
12313 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12314 if ( nodesEdges[i] == nodeTofind )
12316 if ( i == (int)nodesEdges.size() )
12318 found = false; // no predecessor found on front
12321 if (i%2) // odd ==> use the previous one
12322 if (nodesEdges[i-1] < 0)
12326 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12327 nodesEdges[i-1] = -1;
12329 else // even ==> use the next one
12330 if (nodesEdges[i+1] < 0)
12334 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12335 nodesEdges[i+1] = -1;
12341 std::vector<int> nodes;
12342 nodes.push_back(shapeId);
12343 std::list<int>::iterator itl = order.begin();
12344 for (; itl != order.end(); itl++)
12346 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12347 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12349 listOfListOfNodes.push_back(nodes);
12352 // partition geom faces with blocFissure
12353 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12354 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12360 //================================================================================
12362 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12363 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12364 * \return TRUE if operation has been completed successfully, FALSE otherwise
12366 //================================================================================
12368 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12370 // iterates on volume elements and detect all free faces on them
12371 SMESHDS_Mesh* aMesh = GetMeshDS();
12375 ElemFeatures faceType( SMDSAbs_Face );
12376 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12377 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12380 const SMDS_MeshVolume* volume = vIt->next();
12381 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12382 vTool.SetExternalNormal();
12383 const int iQuad = volume->IsQuadratic();
12384 faceType.SetQuad( iQuad );
12385 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12387 if (!vTool.IsFreeFace(iface))
12390 vector<const SMDS_MeshNode *> nodes;
12391 int nbFaceNodes = vTool.NbFaceNodes(iface);
12392 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12394 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12395 nodes.push_back(faceNodes[inode]);
12397 if (iQuad) // add medium nodes
12399 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12400 nodes.push_back(faceNodes[inode]);
12401 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12402 nodes.push_back(faceNodes[8]);
12404 // add new face based on volume nodes
12405 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12407 nbExisted++; // face already exists
12411 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12416 return ( nbFree == ( nbExisted + nbCreated ));
12421 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12423 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12425 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12428 //================================================================================
12430 * \brief Creates missing boundary elements
12431 * \param elements - elements whose boundary is to be checked
12432 * \param dimension - defines type of boundary elements to create
12433 * \param group - a group to store created boundary elements in
12434 * \param targetMesh - a mesh to store created boundary elements in
12435 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12436 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12437 * boundary elements will be copied into the targetMesh
12438 * \param toAddExistingBondary - if true, not only new but also pre-existing
12439 * boundary elements will be added into the new group
12440 * \param aroundElements - if true, elements will be created on boundary of given
12441 * elements else, on boundary of the whole mesh.
12442 * \return nb of added boundary elements
12444 //================================================================================
12446 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12447 Bnd_Dimension dimension,
12448 SMESH_Group* group/*=0*/,
12449 SMESH_Mesh* targetMesh/*=0*/,
12450 bool toCopyElements/*=false*/,
12451 bool toCopyExistingBoundary/*=false*/,
12452 bool toAddExistingBondary/*= false*/,
12453 bool aroundElements/*= false*/)
12455 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12456 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12457 // hope that all elements are of the same type, do not check them all
12458 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12459 throw SALOME_Exception(LOCALIZED("wrong element type"));
12462 toCopyElements = toCopyExistingBoundary = false;
12464 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12465 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12466 int nbAddedBnd = 0;
12468 // editor adding present bnd elements and optionally holding elements to add to the group
12469 SMESH_MeshEditor* presentEditor;
12470 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12471 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12473 SMESH_MesherHelper helper( *myMesh );
12474 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12475 SMDS_VolumeTool vTool;
12476 TIDSortedElemSet avoidSet;
12477 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12480 typedef vector<const SMDS_MeshNode*> TConnectivity;
12481 TConnectivity tgtNodes;
12482 ElemFeatures elemKind( missType ), elemToCopy;
12484 vector<const SMDS_MeshElement*> presentBndElems;
12485 vector<TConnectivity> missingBndElems;
12486 vector<int> freeFacets;
12487 TConnectivity nodes, elemNodes;
12489 SMDS_ElemIteratorPtr eIt;
12490 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12491 else eIt = SMESHUtils::elemSetIterator( elements );
12493 while ( eIt->more() )
12495 const SMDS_MeshElement* elem = eIt->next();
12496 const int iQuad = elem->IsQuadratic();
12497 elemKind.SetQuad( iQuad );
12499 // ------------------------------------------------------------------------------------
12500 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12501 // ------------------------------------------------------------------------------------
12502 presentBndElems.clear();
12503 missingBndElems.clear();
12504 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12505 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12507 const SMDS_MeshElement* otherVol = 0;
12508 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12510 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12511 ( !aroundElements || elements.count( otherVol )))
12513 freeFacets.push_back( iface );
12515 if ( missType == SMDSAbs_Face )
12516 vTool.SetExternalNormal();
12517 for ( size_t i = 0; i < freeFacets.size(); ++i )
12519 int iface = freeFacets[i];
12520 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12521 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12522 if ( missType == SMDSAbs_Edge ) // boundary edges
12524 nodes.resize( 2+iQuad );
12525 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12527 for ( size_t j = 0; j < nodes.size(); ++j )
12528 nodes[ j ] = nn[ i+j ];
12529 if ( const SMDS_MeshElement* edge =
12530 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12531 presentBndElems.push_back( edge );
12533 missingBndElems.push_back( nodes );
12536 else // boundary face
12539 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12540 nodes.push_back( nn[inode] ); // add corner nodes
12542 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12543 nodes.push_back( nn[inode] ); // add medium nodes
12544 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12546 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12548 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12549 SMDSAbs_Face, /*noMedium=*/false ))
12550 presentBndElems.push_back( f );
12552 missingBndElems.push_back( nodes );
12554 if ( targetMesh != myMesh )
12556 // add 1D elements on face boundary to be added to a new mesh
12557 const SMDS_MeshElement* edge;
12558 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12561 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12563 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12564 if ( edge && avoidSet.insert( edge ).second )
12565 presentBndElems.push_back( edge );
12571 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12573 avoidSet.clear(), avoidSet.insert( elem );
12574 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12575 SMDS_MeshElement::iterator() );
12576 elemNodes.push_back( elemNodes[0] );
12577 nodes.resize( 2 + iQuad );
12578 const int nbLinks = elem->NbCornerNodes();
12579 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12581 nodes[0] = elemNodes[iN];
12582 nodes[1] = elemNodes[iN+1+iQuad];
12583 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12584 continue; // not free link
12586 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12587 if ( const SMDS_MeshElement* edge =
12588 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12589 presentBndElems.push_back( edge );
12591 missingBndElems.push_back( nodes );
12595 // ---------------------------------
12596 // 2. Add missing boundary elements
12597 // ---------------------------------
12598 if ( targetMesh != myMesh )
12599 // instead of making a map of nodes in this mesh and targetMesh,
12600 // we create nodes with same IDs.
12601 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12603 TConnectivity& srcNodes = missingBndElems[i];
12604 tgtNodes.resize( srcNodes.size() );
12605 for ( inode = 0; inode < srcNodes.size(); ++inode )
12606 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12607 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12609 /*noMedium=*/false))
12611 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12615 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12617 TConnectivity& nodes = missingBndElems[ i ];
12618 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12620 /*noMedium=*/false))
12622 SMDS_MeshElement* newElem =
12623 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12624 nbAddedBnd += bool( newElem );
12626 // try to set a new element to a shape
12627 if ( myMesh->HasShapeToMesh() )
12630 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12631 const size_t nbN = nodes.size() / (iQuad+1 );
12632 for ( inode = 0; inode < nbN && ok; ++inode )
12634 pair<int, TopAbs_ShapeEnum> i_stype =
12635 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12636 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12637 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12639 if ( ok && mediumShapes.size() > 1 )
12641 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12642 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12643 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12645 if (( ok = ( stype_i->first != stype_i_0.first )))
12646 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12647 aMesh->IndexToShape( stype_i_0.second ));
12650 if ( ok && mediumShapes.begin()->first == missShapeType )
12651 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12655 // ----------------------------------
12656 // 3. Copy present boundary elements
12657 // ----------------------------------
12658 if ( toCopyExistingBoundary )
12659 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12661 const SMDS_MeshElement* e = presentBndElems[i];
12662 tgtNodes.resize( e->NbNodes() );
12663 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12664 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12665 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12667 else // store present elements to add them to a group
12668 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12670 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12673 } // loop on given elements
12675 // ---------------------------------------------
12676 // 4. Fill group with boundary elements
12677 // ---------------------------------------------
12680 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12681 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12682 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12684 tgtEditor.myLastCreatedElems.clear();
12685 tgtEditor2.myLastCreatedElems.clear();
12687 // -----------------------
12688 // 5. Copy given elements
12689 // -----------------------
12690 if ( toCopyElements && targetMesh != myMesh )
12692 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12693 else eIt = SMESHUtils::elemSetIterator( elements );
12694 while (eIt->more())
12696 const SMDS_MeshElement* elem = eIt->next();
12697 tgtNodes.resize( elem->NbNodes() );
12698 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12699 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12700 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12702 tgtEditor.myLastCreatedElems.clear();
12708 //================================================================================
12710 * \brief Copy node position and set \a to node on the same geometry
12712 //================================================================================
12714 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12715 const SMDS_MeshNode* to )
12717 if ( !from || !to ) return;
12719 SMDS_PositionPtr pos = from->GetPosition();
12720 if ( !pos || from->getshapeId() < 1 ) return;
12722 switch ( pos->GetTypeOfPosition() )
12724 case SMDS_TOP_3DSPACE: break;
12726 case SMDS_TOP_FACE:
12728 SMDS_FacePositionPtr fPos = pos;
12729 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12730 fPos->GetUParameter(), fPos->GetVParameter() );
12733 case SMDS_TOP_EDGE:
12735 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12736 SMDS_EdgePositionPtr ePos = pos;
12737 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12740 case SMDS_TOP_VERTEX:
12742 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12745 case SMDS_TOP_UNSPEC: