1 // Copyright (C) 2007-2016 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 <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
107 //=======================================================================
108 //function : SMESH_MeshEditor
110 //=======================================================================
112 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
113 :myMesh( theMesh ) // theMesh may be NULL
117 //================================================================================
119 * \brief Return mesh DS
121 //================================================================================
123 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 return myMesh->GetMeshDS();
129 //================================================================================
131 * \brief Clears myLastCreatedNodes and myLastCreatedElems
133 //================================================================================
135 void SMESH_MeshEditor::ClearLastCreated()
137 SMESHUtils::FreeVector( myLastCreatedElems );
138 SMESHUtils::FreeVector( myLastCreatedNodes );
141 //================================================================================
143 * \brief Initializes members by an existing element
144 * \param [in] elem - the source element
145 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147 //================================================================================
149 SMESH_MeshEditor::ElemFeatures&
150 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
154 myType = elem->GetType();
155 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157 myIsPoly = elem->IsPoly();
160 myIsQuad = elem->IsQuadratic();
161 if ( myType == SMDSAbs_Volume && !basicOnly )
163 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
164 myPolyhedQuantities.swap( quant );
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 == 20) {
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],node[15],
304 node[16],node[17],node[18],node[19],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],node[15],
309 node[16],node[17],node[18],node[19] );
311 else if (nbnode == 27) {
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],
317 node[20],node[21],node[22],node[23],
318 node[24],node[25],node[26], ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19],
324 node[20],node[21],node[22],node[23],
325 node[24],node[25],node[26] );
328 else if ( !features.myIsQuad )
330 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
331 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
335 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
336 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
342 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
343 else e = mesh->AddEdge (node[0], node[1] );
345 else if ( nbnode == 3 ) {
346 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
347 else e = mesh->AddEdge (node[0], node[1], node[2] );
351 case SMDSAbs_0DElement:
353 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
354 else e = mesh->Add0DElement (node[0] );
359 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
360 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
364 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
365 else e = mesh->AddBall (node[0], features.myBallDiameter );
370 if ( e ) myLastCreatedElems.push_back( e );
374 //=======================================================================
378 //=======================================================================
380 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
381 const ElemFeatures& features)
383 vector<const SMDS_MeshNode*> nodes;
384 nodes.reserve( nodeIDs.size() );
385 vector<int>::const_iterator id = nodeIDs.begin();
386 while ( id != nodeIDs.end() ) {
387 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
388 nodes.push_back( node );
392 return AddElement( nodes, features );
395 //=======================================================================
397 //purpose : Remove a node or an element.
398 // Modify a compute state of sub-meshes which become empty
399 //=======================================================================
401 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object.
466 * \param elements - Elements on whose nodes to create 0D elements; if empty,
467 * the all mesh is treated
468 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
469 * \param duplicateElements - to add one more 0D element to a node or not
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems,
475 const bool duplicateElements )
477 SMDS_ElemIteratorPtr elemIt;
478 if ( elements.empty() )
480 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
484 elemIt = SMESHUtils::elemSetIterator( elements );
487 while ( elemIt->more() )
489 const SMDS_MeshElement* e = elemIt->next();
490 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
491 while ( nodeIt->more() )
493 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
494 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
495 if ( duplicateElements || !it0D->more() )
497 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
498 all0DElems.insert( myLastCreatedElems.back() );
500 while ( it0D->more() )
501 all0DElems.insert( it0D->next() );
506 //=======================================================================
507 //function : FindShape
508 //purpose : Return an index of the shape theElem is on
509 // or zero if a shape not found
510 //=======================================================================
512 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
516 SMESHDS_Mesh * aMesh = GetMeshDS();
517 if ( aMesh->ShapeToMesh().IsNull() )
520 int aShapeID = theElem->getshapeId();
524 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
525 if ( sm->Contains( theElem ))
528 if ( theElem->GetType() == SMDSAbs_Node ) {
529 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
532 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
535 TopoDS_Shape aShape; // the shape a node of theElem is on
536 if ( theElem->GetType() != SMDSAbs_Node )
538 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
539 while ( nodeIt->more() ) {
540 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
541 if ((aShapeID = node->getshapeId()) > 0) {
542 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
543 if ( sm->Contains( theElem ))
545 if ( aShape.IsNull() )
546 aShape = aMesh->IndexToShape( aShapeID );
552 // None of nodes is on a proper shape,
553 // find the shape among ancestors of aShape on which a node is
554 if ( !aShape.IsNull() ) {
555 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
556 for ( ; ancIt.More(); ancIt.Next() ) {
557 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
558 if ( sm && sm->Contains( theElem ))
559 return aMesh->ShapeToIndex( ancIt.Value() );
564 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
565 while ( const SMESHDS_SubMesh* sm = smIt->next() )
566 if ( sm->Contains( theElem ))
573 //=======================================================================
574 //function : IsMedium
576 //=======================================================================
578 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
579 const SMDSAbs_ElementType typeToCheck)
581 bool isMedium = false;
582 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
583 while (it->more() && !isMedium ) {
584 const SMDS_MeshElement* elem = it->next();
585 isMedium = elem->IsMediumNode(node);
590 //=======================================================================
591 //function : shiftNodesQuadTria
592 //purpose : Shift nodes in the array corresponded to quadratic triangle
593 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
594 //=======================================================================
596 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
598 const SMDS_MeshNode* nd1 = aNodes[0];
599 aNodes[0] = aNodes[1];
600 aNodes[1] = aNodes[2];
602 const SMDS_MeshNode* nd2 = aNodes[3];
603 aNodes[3] = aNodes[4];
604 aNodes[4] = aNodes[5];
608 //=======================================================================
609 //function : nbEdgeConnectivity
610 //purpose : return number of the edges connected with the theNode.
611 // if theEdges has connections with the other type of the
612 // elements, return -1
613 //=======================================================================
615 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
617 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
619 // while(elemIt->more()) {
624 return theNode->NbInverseElements();
627 //=======================================================================
628 //function : getNodesFromTwoTria
630 //=======================================================================
632 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
633 const SMDS_MeshElement * theTria2,
634 vector< const SMDS_MeshNode*>& N1,
635 vector< const SMDS_MeshNode*>& N2)
637 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
638 if ( N1.size() < 6 ) return false;
639 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
640 if ( N2.size() < 6 ) return false;
642 int sames[3] = {-1,-1,-1};
654 if(nbsames!=2) return false;
656 shiftNodesQuadTria(N1);
658 shiftNodesQuadTria(N1);
661 i = sames[0] + sames[1] + sames[2];
663 shiftNodesQuadTria(N2);
665 // now we receive following N1 and N2 (using numeration as in the image below)
666 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
667 // i.e. first nodes from both arrays form a new diagonal
671 //=======================================================================
672 //function : InverseDiag
673 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
674 // but having other common link.
675 // Return False if args are improper
676 //=======================================================================
678 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
679 const SMDS_MeshElement * theTria2 )
683 if (!theTria1 || !theTria2)
686 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
687 if (!F1) return false;
688 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
689 if (!F2) return false;
690 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
691 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
693 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
694 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
698 // put nodes in array and find out indices of the same ones
699 const SMDS_MeshNode* aNodes [6];
700 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
702 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
703 while ( it->more() ) {
704 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
706 if ( i > 2 ) // theTria2
707 // find same node of theTria1
708 for ( int j = 0; j < 3; j++ )
709 if ( aNodes[ i ] == aNodes[ j ]) {
718 return false; // theTria1 is not a triangle
719 it = theTria2->nodesIterator();
721 if ( i == 6 && it->more() )
722 return false; // theTria2 is not a triangle
725 // find indices of 1,2 and of A,B in theTria1
726 int iA = -1, iB = 0, i1 = 0, i2 = 0;
727 for ( i = 0; i < 6; i++ ) {
728 if ( sameInd [ i ] == -1 ) {
733 if ( iA >= 0) iB = i;
737 // nodes 1 and 2 should not be the same
738 if ( aNodes[ i1 ] == aNodes[ i2 ] )
742 aNodes[ iA ] = aNodes[ i2 ];
744 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
746 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
747 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
751 } // end if(F1 && F2)
753 // check case of quadratic faces
754 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
755 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
757 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
758 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
762 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
763 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
771 vector< const SMDS_MeshNode* > N1;
772 vector< const SMDS_MeshNode* > N2;
773 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
775 // now we receive following N1 and N2 (using numeration as above image)
776 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
777 // i.e. first nodes from both arrays determ new diagonal
779 vector< const SMDS_MeshNode*> N1new( N1.size() );
780 vector< const SMDS_MeshNode*> N2new( N2.size() );
781 N1new.back() = N1.back(); // central node of biquadratic
782 N2new.back() = N2.back();
783 N1new[0] = N1[0]; N2new[0] = N1[0];
784 N1new[1] = N2[0]; N2new[1] = N1[1];
785 N1new[2] = N2[1]; N2new[2] = N2[0];
786 N1new[3] = N1[4]; N2new[3] = N1[3];
787 N1new[4] = N2[3]; N2new[4] = N2[5];
788 N1new[5] = N1[5]; N2new[5] = N1[4];
789 // change nodes in faces
790 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
791 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
793 // move the central node of biquadratic triangle
794 SMESH_MesherHelper helper( *GetMesh() );
795 for ( int is2nd = 0; is2nd < 2; ++is2nd )
797 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
798 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
799 if ( nodes.size() < 7 )
801 helper.SetSubShape( tria->getshapeId() );
802 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
806 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
807 SMESH_TNodeXYZ( nodes[4] ) +
808 SMESH_TNodeXYZ( nodes[5] )) / 3.;
813 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
814 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
815 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
817 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
818 xyz = S->Value( uv.X(), uv.Y() );
819 xyz.Transform( loc );
820 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
821 nodes[6]->getshapeId() > 0 )
822 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
824 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
829 //=======================================================================
830 //function : findTriangles
831 //purpose : find triangles sharing theNode1-theNode2 link
832 //=======================================================================
834 static bool findTriangles(const SMDS_MeshNode * theNode1,
835 const SMDS_MeshNode * theNode2,
836 const SMDS_MeshElement*& theTria1,
837 const SMDS_MeshElement*& theTria2)
839 if ( !theNode1 || !theNode2 ) return false;
841 theTria1 = theTria2 = 0;
843 set< const SMDS_MeshElement* > emap;
844 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
846 const SMDS_MeshElement* elem = it->next();
847 if ( elem->NbCornerNodes() == 3 )
850 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
852 const SMDS_MeshElement* elem = it->next();
853 if ( emap.count( elem )) {
861 // theTria1 must be element with minimum ID
862 if ( theTria2->GetID() < theTria1->GetID() )
863 std::swap( theTria2, theTria1 );
871 //=======================================================================
872 //function : InverseDiag
873 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
874 // with ones built on the same 4 nodes but having other common link.
875 // Return false if proper faces not found
876 //=======================================================================
878 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
879 const SMDS_MeshNode * theNode2)
883 const SMDS_MeshElement *tr1, *tr2;
884 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
887 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
888 if (!F1) return false;
889 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
890 if (!F2) return false;
891 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
892 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
894 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
895 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
899 // put nodes in array
900 // and find indices of 1,2 and of A in tr1 and of B in tr2
901 int i, iA1 = 0, i1 = 0;
902 const SMDS_MeshNode* aNodes1 [3];
903 SMDS_ElemIteratorPtr it;
904 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
905 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
906 if ( aNodes1[ i ] == theNode1 )
907 iA1 = i; // node A in tr1
908 else if ( aNodes1[ i ] != theNode2 )
912 const SMDS_MeshNode* aNodes2 [3];
913 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
914 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
915 if ( aNodes2[ i ] == theNode2 )
916 iB2 = i; // node B in tr2
917 else if ( aNodes2[ i ] != theNode1 )
921 // nodes 1 and 2 should not be the same
922 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
926 aNodes1[ iA1 ] = aNodes2[ i2 ];
928 aNodes2[ iB2 ] = aNodes1[ i1 ];
930 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
931 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
936 // check case of quadratic faces
937 return InverseDiag(tr1,tr2);
940 //=======================================================================
941 //function : getQuadrangleNodes
942 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
943 // fusion of triangles tr1 and tr2 having shared link on
944 // theNode1 and theNode2
945 //=======================================================================
947 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
948 const SMDS_MeshNode * theNode1,
949 const SMDS_MeshNode * theNode2,
950 const SMDS_MeshElement * tr1,
951 const SMDS_MeshElement * tr2 )
953 if( tr1->NbNodes() != tr2->NbNodes() )
955 // find the 4-th node to insert into tr1
956 const SMDS_MeshNode* n4 = 0;
957 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
959 while ( !n4 && i<3 ) {
960 const SMDS_MeshNode * n = cast2Node( it->next() );
962 bool isDiag = ( n == theNode1 || n == theNode2 );
966 // Make an array of nodes to be in a quadrangle
967 int iNode = 0, iFirstDiag = -1;
968 it = tr1->nodesIterator();
971 const SMDS_MeshNode * n = cast2Node( it->next() );
973 bool isDiag = ( n == theNode1 || n == theNode2 );
975 if ( iFirstDiag < 0 )
977 else if ( iNode - iFirstDiag == 1 )
978 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
980 else if ( n == n4 ) {
981 return false; // tr1 and tr2 should not have all the same nodes
983 theQuadNodes[ iNode++ ] = n;
985 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
986 theQuadNodes[ iNode ] = n4;
991 //=======================================================================
992 //function : DeleteDiag
993 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
994 // with a quadrangle built on the same 4 nodes.
995 // Return false if proper faces not found
996 //=======================================================================
998 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
999 const SMDS_MeshNode * theNode2)
1003 const SMDS_MeshElement *tr1, *tr2;
1004 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1007 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1008 if (!F1) return false;
1009 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1010 if (!F2) return false;
1011 SMESHDS_Mesh * aMesh = GetMeshDS();
1013 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1014 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1016 const SMDS_MeshNode* aNodes [ 4 ];
1017 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_MeshElement* newElem = 0;
1021 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1022 myLastCreatedElems.push_back(newElem);
1023 AddToSameGroups( newElem, tr1, aMesh );
1024 int aShapeId = tr1->getshapeId();
1027 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1029 aMesh->RemoveElement( tr1 );
1030 aMesh->RemoveElement( tr2 );
1035 // check case of quadratic faces
1036 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1038 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1042 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1043 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1051 vector< const SMDS_MeshNode* > N1;
1052 vector< const SMDS_MeshNode* > N2;
1053 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1055 // now we receive following N1 and N2 (using numeration as above image)
1056 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1057 // i.e. first nodes from both arrays determ new diagonal
1059 const SMDS_MeshNode* aNodes[8];
1069 const SMDS_MeshElement* newElem = 0;
1070 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1071 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1072 myLastCreatedElems.push_back(newElem);
1073 AddToSameGroups( newElem, tr1, aMesh );
1074 int aShapeId = tr1->getshapeId();
1077 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1079 aMesh->RemoveElement( tr1 );
1080 aMesh->RemoveElement( tr2 );
1082 // remove middle node (9)
1083 GetMeshDS()->RemoveNode( N1[4] );
1088 //=======================================================================
1089 //function : Reorient
1090 //purpose : Reverse theElement orientation
1091 //=======================================================================
1093 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1099 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1100 if ( !it || !it->more() )
1103 const SMDSAbs_ElementType type = theElem->GetType();
1104 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1107 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1108 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1110 const SMDS_VtkVolume* aPolyedre =
1111 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1113 MESSAGE("Warning: bad volumic element");
1116 const int nbFaces = aPolyedre->NbFaces();
1117 vector<const SMDS_MeshNode *> poly_nodes;
1118 vector<int> quantities (nbFaces);
1120 // reverse each face of the polyedre
1121 for (int iface = 1; iface <= nbFaces; iface++) {
1122 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123 quantities[iface - 1] = nbFaceNodes;
1125 for (inode = nbFaceNodes; inode >= 1; inode--) {
1126 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127 poly_nodes.push_back(curNode);
1130 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1132 else // other elements
1134 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136 if ( interlace.empty() )
1138 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1142 SMDS_MeshCell::applyInterlace( interlace, nodes );
1144 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1149 //================================================================================
1151 * \brief Reorient faces.
1152 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153 * \param theDirection - desired direction of normal of \a theFace
1154 * \param theFace - one of \a theFaces that sould be oriented according to
1155 * \a theDirection and whose orientation defines orientation of other faces
1156 * \return number of reoriented faces.
1158 //================================================================================
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1161 const gp_Dir& theDirection,
1162 const SMDS_MeshElement * theFace)
1165 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1167 if ( theFaces.empty() )
1169 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1170 while ( fIt->more() )
1171 theFaces.insert( theFaces.end(), fIt->next() );
1174 // orient theFace according to theDirection
1176 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177 if ( normal * theDirection.XYZ() < 0 )
1178 nbReori += Reorient( theFace );
1180 // Orient other faces
1182 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183 TIDSortedElemSet avoidSet;
1184 set< SMESH_TLink > checkedLinks;
1185 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1187 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188 theFaces.erase( theFace );
1189 startFaces.insert( theFace );
1191 int nodeInd1, nodeInd2;
1192 const SMDS_MeshElement* otherFace;
1193 vector< const SMDS_MeshElement* > facesNearLink;
1194 vector< std::pair< int, int > > nodeIndsOfFace;
1196 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197 while ( !startFaces.empty() )
1199 startFace = startFaces.begin();
1200 theFace = *startFace;
1201 startFaces.erase( startFace );
1202 if ( !visitedFaces.insert( theFace ).second )
1206 avoidSet.insert(theFace);
1208 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1210 const int nbNodes = theFace->NbCornerNodes();
1211 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1213 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214 linkIt_isNew = checkedLinks.insert( link );
1215 if ( !linkIt_isNew.second )
1217 // link has already been checked and won't be encountered more
1218 // if the group (theFaces) is manifold
1219 //checkedLinks.erase( linkIt_isNew.first );
1223 facesNearLink.clear();
1224 nodeIndsOfFace.clear();
1225 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1227 &nodeInd1, &nodeInd2 )))
1228 if ( otherFace != theFace)
1230 facesNearLink.push_back( otherFace );
1231 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232 avoidSet.insert( otherFace );
1234 if ( facesNearLink.size() > 1 )
1236 // NON-MANIFOLD mesh shell !
1237 // select a face most co-directed with theFace,
1238 // other faces won't be visited this time
1240 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241 double proj, maxProj = -1;
1242 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244 if (( proj = Abs( NF * NOF )) > maxProj ) {
1246 otherFace = facesNearLink[i];
1247 nodeInd1 = nodeIndsOfFace[i].first;
1248 nodeInd2 = nodeIndsOfFace[i].second;
1251 // not to visit rejected faces
1252 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254 visitedFaces.insert( facesNearLink[i] );
1256 else if ( facesNearLink.size() == 1 )
1258 otherFace = facesNearLink[0];
1259 nodeInd1 = nodeIndsOfFace.back().first;
1260 nodeInd2 = nodeIndsOfFace.back().second;
1262 if ( otherFace && otherFace != theFace)
1264 // link must be reverse in otherFace if orientation ot otherFace
1265 // is same as that of theFace
1266 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1268 nbReori += Reorient( otherFace );
1270 startFaces.insert( otherFace );
1273 std::swap( link.first, link.second ); // reverse the link
1279 //================================================================================
1281 * \brief Reorient faces basing on orientation of adjacent volumes.
1282 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283 * \param theVolumes - reference volumes.
1284 * \param theOutsideNormal - to orient faces to have their normal
1285 * pointing either \a outside or \a inside the adjacent volumes.
1286 * \return number of reoriented faces.
1288 //================================================================================
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291 TIDSortedElemSet & theVolumes,
1292 const bool theOutsideNormal)
1296 SMDS_ElemIteratorPtr faceIt;
1297 if ( theFaces.empty() )
1298 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1300 faceIt = SMESHUtils::elemSetIterator( theFaces );
1302 vector< const SMDS_MeshNode* > faceNodes;
1303 TIDSortedElemSet checkedVolumes;
1304 set< const SMDS_MeshNode* > faceNodesSet;
1305 SMDS_VolumeTool volumeTool;
1307 while ( faceIt->more() ) // loop on given faces
1309 const SMDS_MeshElement* face = faceIt->next();
1310 if ( face->GetType() != SMDSAbs_Face )
1313 const size_t nbCornersNodes = face->NbCornerNodes();
1314 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1316 checkedVolumes.clear();
1317 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318 while ( vIt->more() )
1320 const SMDS_MeshElement* volume = vIt->next();
1322 if ( !checkedVolumes.insert( volume ).second )
1324 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1327 // is volume adjacent?
1328 bool allNodesCommon = true;
1329 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331 if ( !allNodesCommon )
1334 // get nodes of a corresponding volume facet
1335 faceNodesSet.clear();
1336 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337 volumeTool.Set( volume );
1338 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339 if ( facetID < 0 ) continue;
1340 volumeTool.SetExternalNormal();
1341 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1343 // compare order of faceNodes and facetNodes
1344 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1346 for ( int i = 0; i < 2; ++i )
1348 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350 if ( faceNodes[ iN ] == n )
1356 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357 if ( isOutside != theOutsideNormal )
1358 nbReori += Reorient( face );
1360 } // loop on given faces
1365 //=======================================================================
1366 //function : getBadRate
1368 //=======================================================================
1370 static double getBadRate (const SMDS_MeshElement* theElem,
1371 SMESH::Controls::NumericalFunctorPtr& theCrit)
1373 SMESH::Controls::TSequenceOfXYZ P;
1374 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1376 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose : Cut quadrangles into triangles.
1383 // theCrit is used to select a diagonal to cut
1384 //=======================================================================
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1387 SMESH::Controls::NumericalFunctorPtr theCrit)
1391 if ( !theCrit.get() )
1394 SMESHDS_Mesh * aMesh = GetMeshDS();
1395 Handle(Geom_Surface) surface;
1396 SMESH_MesherHelper helper( *GetMesh() );
1398 myLastCreatedElems.reserve( theElems.size() * 2 );
1400 TIDSortedElemSet::iterator itElem;
1401 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1403 const SMDS_MeshElement* elem = *itElem;
1404 if ( !elem || elem->GetType() != SMDSAbs_Face )
1406 if ( elem->NbCornerNodes() != 4 )
1409 // retrieve element nodes
1410 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1412 // compare two sets of possible triangles
1413 double aBadRate1, aBadRate2; // to what extent a set is bad
1414 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1418 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1422 const int aShapeId = FindShape( elem );
1423 const SMDS_MeshElement* newElem1 = 0;
1424 const SMDS_MeshElement* newElem2 = 0;
1426 if ( !elem->IsQuadratic() ) // split liner quadrangle
1428 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430 if ( aBadRate1 <= aBadRate2 ) {
1431 // tr1 + tr2 is better
1432 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1436 // tr3 + tr4 is better
1437 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1441 else // split quadratic quadrangle
1443 helper.SetIsQuadratic( true );
1444 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1446 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447 if ( aNodes.size() == 9 )
1449 helper.SetIsBiQuadratic( true );
1450 if ( aBadRate1 <= aBadRate2 )
1451 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1453 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1455 // create a new element
1456 if ( aBadRate1 <= aBadRate2 ) {
1457 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1461 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1466 // care of a new element
1468 myLastCreatedElems.push_back(newElem1);
1469 myLastCreatedElems.push_back(newElem2);
1470 AddToSameGroups( newElem1, elem, aMesh );
1471 AddToSameGroups( newElem2, elem, aMesh );
1473 // put a new triangle on the same shape
1475 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1478 aMesh->RemoveElement( elem );
1483 //=======================================================================
1485 * \brief Split each of given quadrangles into 4 triangles.
1486 * \param theElems - The faces to be splitted. If empty all faces are split.
1488 //=======================================================================
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1493 myLastCreatedElems.reserve( theElems.size() * 4 );
1495 SMESH_MesherHelper helper( *GetMesh() );
1496 helper.SetElementsOnShape( true );
1498 SMDS_ElemIteratorPtr faceIt;
1499 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500 else faceIt = SMESHUtils::elemSetIterator( theElems );
1503 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1505 vector< const SMDS_MeshNode* > nodes;
1506 SMESHDS_SubMesh* subMeshDS = 0;
1508 Handle(Geom_Surface) surface;
1509 TopLoc_Location loc;
1511 while ( faceIt->more() )
1513 const SMDS_MeshElement* quad = faceIt->next();
1514 if ( !quad || quad->NbCornerNodes() != 4 )
1517 // get a surface the quad is on
1519 if ( quad->getshapeId() < 1 )
1522 helper.SetSubShape( 0 );
1525 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1527 helper.SetSubShape( quad->getshapeId() );
1528 if ( !helper.GetSubShape().IsNull() &&
1529 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1531 F = TopoDS::Face( helper.GetSubShape() );
1532 surface = BRep_Tool::Surface( F, loc );
1533 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1537 helper.SetSubShape( 0 );
1542 // create a central node
1544 const SMDS_MeshNode* nCentral;
1545 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1547 if ( nodes.size() == 9 )
1549 nCentral = nodes.back();
1556 for ( ; iN < nodes.size(); ++iN )
1557 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1559 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1562 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563 xyz[0], xyz[1], xyz[2], xyz[3],
1564 xyz[4], xyz[5], xyz[6], xyz[7] );
1568 for ( ; iN < nodes.size(); ++iN )
1569 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1571 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1574 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575 uv[0], uv[1], uv[2], uv[3],
1576 uv[4], uv[5], uv[6], uv[7] );
1578 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1582 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583 uv[8].X(), uv[8].Y() );
1584 myLastCreatedNodes.push_back( nCentral );
1587 // create 4 triangles
1589 helper.SetIsQuadratic ( nodes.size() > 4 );
1590 helper.SetIsBiQuadratic( nodes.size() == 9 );
1591 if ( helper.GetIsQuadratic() )
1592 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1594 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1596 for ( int i = 0; i < 4; ++i )
1598 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1601 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602 myLastCreatedElems.push_back( tria );
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose : Find better diagonal for cutting.
1610 //=======================================================================
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1613 SMESH::Controls::NumericalFunctorPtr theCrit)
1620 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1623 if( theQuad->NbNodes()==4 ||
1624 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1626 // retrieve element nodes
1627 const SMDS_MeshNode* aNodes [4];
1628 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1630 //while (itN->more())
1632 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1634 // compare two sets of possible triangles
1635 double aBadRate1, aBadRate2; // to what extent a set is bad
1636 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1640 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646 return 1; // diagonal 1-3
1648 return 2; // diagonal 2-4
1655 // Methods of splitting volumes into tetra
1657 const int theHexTo5_1[5*4+1] =
1659 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1661 const int theHexTo5_2[5*4+1] =
1663 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1665 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1667 const int theHexTo6_1[6*4+1] =
1669 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
1671 const int theHexTo6_2[6*4+1] =
1673 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
1675 const int theHexTo6_3[6*4+1] =
1677 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
1679 const int theHexTo6_4[6*4+1] =
1681 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
1683 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1685 const int thePyraTo2_1[2*4+1] =
1687 0, 1, 2, 4, 0, 2, 3, 4, -1
1689 const int thePyraTo2_2[2*4+1] =
1691 1, 2, 3, 4, 1, 3, 0, 4, -1
1693 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1695 const int thePentaTo3_1[3*4+1] =
1697 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1699 const int thePentaTo3_2[3*4+1] =
1701 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1703 const int thePentaTo3_3[3*4+1] =
1705 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1707 const int thePentaTo3_4[3*4+1] =
1709 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1711 const int thePentaTo3_5[3*4+1] =
1713 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1715 const int thePentaTo3_6[3*4+1] =
1717 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1719 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1722 // Methods of splitting hexahedron into prisms
1724 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1726 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
1728 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1730 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
1732 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1734 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
1737 const int theHexTo2Prisms_BT_1[6*2+1] =
1739 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1741 const int theHexTo2Prisms_BT_2[6*2+1] =
1743 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1745 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1747 const int theHexTo2Prisms_LR_1[6*2+1] =
1749 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1751 const int theHexTo2Prisms_LR_2[6*2+1] =
1753 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1755 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1757 const int theHexTo2Prisms_FB_1[6*2+1] =
1759 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1761 const int theHexTo2Prisms_FB_2[6*2+1] =
1763 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1765 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1768 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1771 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1774 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1780 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781 bool _baryNode; //!< additional node is to be created at cell barycenter
1782 bool _ownConn; //!< to delete _connectivity in destructor
1783 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1785 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788 bool hasFacet( const TTriangleFacet& facet ) const
1790 if ( _nbCorners == 4 )
1792 const int* tetConn = _connectivity;
1793 for ( ; tetConn[0] >= 0; tetConn += 4 )
1794 if (( facet.contains( tetConn[0] ) +
1795 facet.contains( tetConn[1] ) +
1796 facet.contains( tetConn[2] ) +
1797 facet.contains( tetConn[3] )) == 3 )
1800 else // prism, _nbCorners == 6
1802 const int* prismConn = _connectivity;
1803 for ( ; prismConn[0] >= 0; prismConn += 6 )
1805 if (( facet.contains( prismConn[0] ) &&
1806 facet.contains( prismConn[1] ) &&
1807 facet.contains( prismConn[2] ))
1809 ( facet.contains( prismConn[3] ) &&
1810 facet.contains( prismConn[4] ) &&
1811 facet.contains( prismConn[5] )))
1819 //=======================================================================
1821 * \brief return TSplitMethod for the given element to split into tetrahedra
1823 //=======================================================================
1825 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1827 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1829 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830 // an edge and a face barycenter; tertaherdons are based on triangles and
1831 // a volume barycenter
1832 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1834 // Find out how adjacent volumes are split
1836 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1840 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842 if ( nbNodes < 4 ) continue;
1844 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845 const int* nInd = vol.GetFaceNodesIndices( iF );
1848 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1855 int iCom = 0; // common node of triangle faces to split into
1856 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1858 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1859 nInd[ iQ * ( (iCom+1)%nbNodes )],
1860 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1862 nInd[ iQ * ( (iCom+2)%nbNodes )],
1863 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1866 triaSplits.push_back( t012 );
1867 triaSplits.push_back( t023 );
1872 if ( !triaSplits.empty() )
1873 hasAdjacentSplits = true;
1876 // Among variants of split method select one compliant with adjacent volumes
1878 TSplitMethod method;
1879 if ( !vol.Element()->IsPoly() && !is24TetMode )
1881 int nbVariants = 2, nbTet = 0;
1882 const int** connVariants = 0;
1883 switch ( vol.Element()->GetEntityType() )
1885 case SMDSEntity_Hexa:
1886 case SMDSEntity_Quad_Hexa:
1887 case SMDSEntity_TriQuad_Hexa:
1888 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889 connVariants = theHexTo5, nbTet = 5;
1891 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1893 case SMDSEntity_Pyramid:
1894 case SMDSEntity_Quad_Pyramid:
1895 connVariants = thePyraTo2; nbTet = 2;
1897 case SMDSEntity_Penta:
1898 case SMDSEntity_Quad_Penta:
1899 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1904 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1906 // check method compliancy with adjacent tetras,
1907 // all found splits must be among facets of tetras described by this method
1908 method = TSplitMethod( nbTet, connVariants[variant] );
1909 if ( hasAdjacentSplits && method._nbSplits > 0 )
1911 bool facetCreated = true;
1912 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1914 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1915 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1916 facetCreated = method.hasFacet( *facet );
1918 if ( !facetCreated )
1919 method = TSplitMethod(0); // incompatible method
1923 if ( method._nbSplits < 1 )
1925 // No standard method is applicable, use a generic solution:
1926 // each facet of a volume is split into triangles and
1927 // each of triangles and a volume barycenter form a tetrahedron.
1929 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1931 int* connectivity = new int[ maxTetConnSize + 1 ];
1932 method._connectivity = connectivity;
1933 method._ownConn = true;
1934 method._baryNode = !isHex27; // to create central node or not
1937 int baryCenInd = vol.NbNodes() - int( isHex27 );
1938 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1940 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1941 const int* nInd = vol.GetFaceNodesIndices( iF );
1942 // find common node of triangle facets of tetra to create
1943 int iCommon = 0; // index in linear numeration
1944 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1945 if ( !triaSplits.empty() )
1948 const TTriangleFacet* facet = &triaSplits.front();
1949 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1950 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1951 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1954 else if ( nbNodes > 3 && !is24TetMode )
1956 // find the best method of splitting into triangles by aspect ratio
1957 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1958 map< double, int > badness2iCommon;
1959 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1960 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1961 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1964 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1966 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1967 nodes[ iQ*((iLast-1)%nbNodes)],
1968 nodes[ iQ*((iLast )%nbNodes)]);
1969 badness += getBadRate( &tria, aspectRatio );
1971 badness2iCommon.insert( make_pair( badness, iCommon ));
1973 // use iCommon with lowest badness
1974 iCommon = badness2iCommon.begin()->second;
1976 if ( iCommon >= nbNodes )
1977 iCommon = 0; // something wrong
1979 // fill connectivity of tetrahedra based on a current face
1980 int nbTet = nbNodes - 2;
1981 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1986 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1987 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1991 method._faceBaryNode[ iF ] = 0;
1992 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1995 for ( int i = 0; i < nbTet; ++i )
1997 int i1 = i, i2 = (i+1) % nbNodes;
1998 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1999 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2000 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2001 connectivity[ connSize++ ] = faceBaryCenInd;
2002 connectivity[ connSize++ ] = baryCenInd;
2007 for ( int i = 0; i < nbTet; ++i )
2009 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2010 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2011 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2012 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2013 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2014 connectivity[ connSize++ ] = baryCenInd;
2017 method._nbSplits += nbTet;
2019 } // loop on volume faces
2021 connectivity[ connSize++ ] = -1;
2023 } // end of generic solution
2027 //=======================================================================
2029 * \brief return TSplitMethod to split haxhedron into prisms
2031 //=======================================================================
2033 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2034 const int methodFlags,
2035 const int facetToSplit)
2037 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2039 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2041 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2043 static TSplitMethod to4methods[4]; // order BT, LR, FB
2044 if ( to4methods[iF]._nbSplits == 0 )
2048 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2049 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2050 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2053 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2054 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2055 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2058 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2059 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2060 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2062 default: return to4methods[3];
2064 to4methods[iF]._nbSplits = 4;
2065 to4methods[iF]._nbCorners = 6;
2067 return to4methods[iF];
2069 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2071 TSplitMethod method;
2073 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2075 const int nbVariants = 2, nbSplits = 2;
2076 const int** connVariants = 0;
2078 case 0: connVariants = theHexTo2Prisms_BT; break;
2079 case 1: connVariants = theHexTo2Prisms_LR; break;
2080 case 2: connVariants = theHexTo2Prisms_FB; break;
2081 default: return method;
2084 // look for prisms adjacent via facetToSplit and an opposite one
2085 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2087 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2088 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2089 if ( nbNodes != 4 ) return method;
2091 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2092 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2093 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2095 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2102 // there are adjacent prism
2103 for ( int variant = 0; variant < nbVariants; ++variant )
2105 // check method compliancy with adjacent prisms,
2106 // the found prism facets must be among facets of prisms described by current method
2107 method._nbSplits = nbSplits;
2108 method._nbCorners = 6;
2109 method._connectivity = connVariants[ variant ];
2110 if ( method.hasFacet( *t ))
2115 // No adjacent prisms. Select a variant with a best aspect ratio.
2117 double badness[2] = { 0., 0. };
2118 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2119 const SMDS_MeshNode** nodes = vol.GetNodes();
2120 for ( int variant = 0; variant < nbVariants; ++variant )
2121 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2124 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2126 method._connectivity = connVariants[ variant ];
2127 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2129 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2131 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2134 badness[ variant ] += getBadRate( &tria, aspectRatio );
2136 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2138 method._nbSplits = nbSplits;
2139 method._nbCorners = 6;
2140 method._connectivity = connVariants[ iBetter ];
2145 //================================================================================
2147 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2149 //================================================================================
2151 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2152 const SMDSAbs_GeometryType geom ) const
2154 // find the tetrahedron including the three nodes of facet
2155 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2156 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2157 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2158 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2159 while ( volIt1->more() )
2161 const SMDS_MeshElement* v = volIt1->next();
2162 if ( v->GetGeomType() != geom )
2164 const int lastCornerInd = v->NbCornerNodes() - 1;
2165 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2166 continue; // medium node not allowed
2167 const int ind2 = v->GetNodeIndex( n2 );
2168 if ( ind2 < 0 || lastCornerInd < ind2 )
2170 const int ind3 = v->GetNodeIndex( n3 );
2171 if ( ind3 < 0 || lastCornerInd < ind3 )
2178 //=======================================================================
2180 * \brief A key of a face of volume
2182 //=======================================================================
2184 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2186 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2188 TIDSortedNodeSet sortedNodes;
2189 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2190 int nbNodes = vol.NbFaceNodes( iF );
2191 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2192 for ( int i = 0; i < nbNodes; i += iQ )
2193 sortedNodes.insert( fNodes[i] );
2194 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2195 first.first = (*(n++))->GetID();
2196 first.second = (*(n++))->GetID();
2197 second.first = (*(n++))->GetID();
2198 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2203 //=======================================================================
2204 //function : SplitVolumes
2205 //purpose : Split volume elements into tetrahedra or prisms.
2206 // If facet ID < 0, element is split into tetrahedra,
2207 // else a hexahedron is split into prisms so that the given facet is
2208 // split into triangles
2209 //=======================================================================
2211 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2212 const int theMethodFlags)
2214 SMDS_VolumeTool volTool;
2215 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2216 fHelper.ToFixNodeParameters( true );
2218 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2219 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2221 SMESH_SequenceOfElemPtr newNodes, newElems;
2223 // map face of volume to it's baricenrtic node
2224 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2226 vector<const SMDS_MeshElement* > splitVols;
2228 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2229 for ( ; elem2facet != theElems.end(); ++elem2facet )
2231 const SMDS_MeshElement* elem = elem2facet->first;
2232 const int facetToSplit = elem2facet->second;
2233 if ( elem->GetType() != SMDSAbs_Volume )
2235 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2236 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2239 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2241 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2242 getTetraSplitMethod( volTool, theMethodFlags ) :
2243 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2244 if ( splitMethod._nbSplits < 1 ) continue;
2246 // find submesh to add new tetras to
2247 if ( !subMesh || !subMesh->Contains( elem ))
2249 int shapeID = FindShape( elem );
2250 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2251 subMesh = GetMeshDS()->MeshElements( shapeID );
2254 if ( elem->IsQuadratic() )
2257 // add quadratic links to the helper
2258 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2260 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2261 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2262 for ( int iN = 0; iN < nbN; iN += iQ )
2263 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2265 helper.SetIsQuadratic( true );
2270 helper.SetIsQuadratic( false );
2272 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2273 volTool.GetNodes() + elem->NbNodes() );
2274 helper.SetElementsOnShape( true );
2275 if ( splitMethod._baryNode )
2277 // make a node at barycenter
2278 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2279 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2280 nodes.push_back( gcNode );
2281 newNodes.push_back( gcNode );
2283 if ( !splitMethod._faceBaryNode.empty() )
2285 // make or find baricentric nodes of faces
2286 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2287 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2289 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2290 volFace2BaryNode.insert
2291 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2294 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2295 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2297 nodes.push_back( iF_n->second = f_n->second );
2302 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2303 const int* volConn = splitMethod._connectivity;
2304 if ( splitMethod._nbCorners == 4 ) // tetra
2305 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2306 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2307 nodes[ volConn[1] ],
2308 nodes[ volConn[2] ],
2309 nodes[ volConn[3] ]));
2311 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2312 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2313 nodes[ volConn[1] ],
2314 nodes[ volConn[2] ],
2315 nodes[ volConn[3] ],
2316 nodes[ volConn[4] ],
2317 nodes[ volConn[5] ]));
2319 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2321 // Split faces on sides of the split volume
2323 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2324 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2326 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2327 if ( nbNodes < 4 ) continue;
2329 // find an existing face
2330 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2331 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2332 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2333 /*noMedium=*/false))
2336 helper.SetElementsOnShape( false );
2337 vector< const SMDS_MeshElement* > triangles;
2339 // find submesh to add new triangles in
2340 if ( !fSubMesh || !fSubMesh->Contains( face ))
2342 int shapeID = FindShape( face );
2343 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2345 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2346 if ( iF_n != splitMethod._faceBaryNode.end() )
2348 const SMDS_MeshNode *baryNode = iF_n->second;
2349 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2351 const SMDS_MeshNode* n1 = fNodes[iN];
2352 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2353 const SMDS_MeshNode *n3 = baryNode;
2354 if ( !volTool.IsFaceExternal( iF ))
2356 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2358 if ( fSubMesh ) // update position of the bary node on geometry
2361 subMesh->RemoveNode( baryNode, false );
2362 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2363 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2364 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2366 fHelper.SetSubShape( s );
2367 gp_XY uv( 1e100, 1e100 );
2369 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2370 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2373 // node is too far from the surface
2374 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2375 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2376 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2383 // among possible triangles create ones discribed by split method
2384 const int* nInd = volTool.GetFaceNodesIndices( iF );
2385 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2386 int iCom = 0; // common node of triangle faces to split into
2387 list< TTriangleFacet > facets;
2388 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2390 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2391 nInd[ iQ * ( (iCom+1)%nbNodes )],
2392 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2393 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2394 nInd[ iQ * ( (iCom+2)%nbNodes )],
2395 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2396 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2398 facets.push_back( t012 );
2399 facets.push_back( t023 );
2400 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2401 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2402 nInd[ iQ * ((iLast-1)%nbNodes )],
2403 nInd[ iQ * ((iLast )%nbNodes )]));
2407 list< TTriangleFacet >::iterator facet = facets.begin();
2408 if ( facet == facets.end() )
2410 for ( ; facet != facets.end(); ++facet )
2412 if ( !volTool.IsFaceExternal( iF ))
2413 swap( facet->_n2, facet->_n3 );
2414 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2415 volNodes[ facet->_n2 ],
2416 volNodes[ facet->_n3 ]));
2419 for ( size_t i = 0; i < triangles.size(); ++i )
2421 if ( !triangles[ i ]) continue;
2423 fSubMesh->AddElement( triangles[ i ]);
2424 newElems.push_back( triangles[ i ]);
2426 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2427 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2429 } // while a face based on facet nodes exists
2430 } // loop on volume faces to split them into triangles
2432 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2434 if ( geomType == SMDSEntity_TriQuad_Hexa )
2436 // remove medium nodes that could become free
2437 for ( int i = 20; i < volTool.NbNodes(); ++i )
2438 if ( volNodes[i]->NbInverseElements() == 0 )
2439 GetMeshDS()->RemoveNode( volNodes[i] );
2441 } // loop on volumes to split
2443 myLastCreatedNodes = newNodes;
2444 myLastCreatedElems = newElems;
2447 //=======================================================================
2448 //function : GetHexaFacetsToSplit
2449 //purpose : For hexahedra that will be split into prisms, finds facets to
2450 // split into triangles. Only hexahedra adjacent to the one closest
2451 // to theFacetNormal.Location() are returned.
2452 //param [in,out] theHexas - the hexahedra
2453 //param [in] theFacetNormal - facet normal
2454 //param [out] theFacets - the hexahedra and found facet IDs
2455 //=======================================================================
2457 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2458 const gp_Ax1& theFacetNormal,
2459 TFacetOfElem & theFacets)
2461 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2463 // Find a hexa closest to the location of theFacetNormal
2465 const SMDS_MeshElement* startHex;
2467 // get SMDS_ElemIteratorPtr on theHexas
2468 typedef const SMDS_MeshElement* TValue;
2469 typedef TIDSortedElemSet::iterator TSetIterator;
2470 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2471 typedef SMDS_MeshElement::GeomFilter TFilter;
2472 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2473 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2474 ( new TElemSetIter( theHexas.begin(),
2476 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2478 SMESH_ElementSearcher* searcher =
2479 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2481 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2486 throw SALOME_Exception( THIS_METHOD "startHex not found");
2489 // Select a facet of startHex by theFacetNormal
2491 SMDS_VolumeTool vTool( startHex );
2492 double norm[3], dot, maxDot = 0;
2494 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2495 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2497 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2505 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2507 // Fill theFacets starting from facetID of startHex
2509 // facets used for seach of volumes adjacent to already treated ones
2510 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2511 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2512 TFacetMap facetsToCheck;
2514 set<const SMDS_MeshNode*> facetNodes;
2515 const SMDS_MeshElement* curHex;
2517 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2521 // move in two directions from startHex via facetID
2522 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2525 int curFacet = facetID;
2526 if ( is2nd ) // do not treat startHex twice
2528 vTool.Set( curHex );
2529 if ( vTool.IsFreeFace( curFacet, &curHex ))
2535 vTool.GetFaceNodes( curFacet, facetNodes );
2536 vTool.Set( curHex );
2537 curFacet = vTool.GetFaceIndex( facetNodes );
2542 // store a facet to split
2543 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2545 theFacets.insert( make_pair( curHex, -1 ));
2548 if ( !allHex && !theHexas.count( curHex ))
2551 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2552 theFacets.insert( make_pair( curHex, curFacet ));
2553 if ( !facetIt2isNew.second )
2556 // remember not-to-split facets in facetsToCheck
2557 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2558 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2560 if ( iF == curFacet && iF == oppFacet )
2562 TVolumeFaceKey facetKey ( vTool, iF );
2563 TElemFacets elemFacet( facetIt2isNew.first, iF );
2564 pair< TFacetMap::iterator, bool > it2isnew =
2565 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2566 if ( !it2isnew.second )
2567 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2569 // pass to a volume adjacent via oppFacet
2570 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2576 // get a new curFacet
2577 vTool.GetFaceNodes( oppFacet, facetNodes );
2578 vTool.Set( curHex );
2579 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2582 } // move in two directions from startHex via facetID
2584 // Find a new startHex by facetsToCheck
2588 TFacetMap::iterator fIt = facetsToCheck.begin();
2589 while ( !startHex && fIt != facetsToCheck.end() )
2591 const TElemFacets& elemFacets = fIt->second;
2592 const SMDS_MeshElement* hex = elemFacets.first->first;
2593 int splitFacet = elemFacets.first->second;
2594 int lateralFacet = elemFacets.second;
2595 facetsToCheck.erase( fIt );
2596 fIt = facetsToCheck.begin();
2599 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2600 curHex->GetGeomType() != SMDSGeom_HEXA )
2602 if ( !allHex && !theHexas.count( curHex ))
2607 // find a facet of startHex to split
2609 set<const SMDS_MeshNode*> lateralNodes;
2610 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2611 vTool.GetFaceNodes( splitFacet, facetNodes );
2612 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2613 vTool.Set( startHex );
2614 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2616 // look for a facet of startHex having common nodes with facetNodes
2617 // but not lateralFacet
2618 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2620 if ( iF == lateralFacet )
2622 int nbCommonNodes = 0;
2623 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2624 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2625 nbCommonNodes += facetNodes.count( nn[ iN ]);
2627 if ( nbCommonNodes >= 2 )
2634 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2636 } // while ( startHex )
2643 //================================================================================
2645 * \brief Selects nodes of several elements according to a given interlace
2646 * \param [in] srcNodes - nodes to select from
2647 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2648 * \param [in] interlace - indices of nodes for all elements
2649 * \param [in] nbElems - nb of elements
2650 * \param [in] nbNodes - nb of nodes in each element
2651 * \param [in] mesh - the mesh
2652 * \param [out] elemQueue - a list to push elements found by the selected nodes
2653 * \param [in] type - type of elements to look for
2655 //================================================================================
2657 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2658 vector< const SMDS_MeshNode* >* tgtNodesVec,
2659 const int* interlace,
2662 SMESHDS_Mesh* mesh = 0,
2663 list< const SMDS_MeshElement* >* elemQueue=0,
2664 SMDSAbs_ElementType type=SMDSAbs_All)
2666 for ( int iE = 0; iE < nbElems; ++iE )
2668 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2669 const int* select = & interlace[iE*nbNodes];
2670 elemNodes.resize( nbNodes );
2671 for ( int iN = 0; iN < nbNodes; ++iN )
2672 elemNodes[iN] = srcNodes[ select[ iN ]];
2674 const SMDS_MeshElement* e;
2676 for ( int iE = 0; iE < nbElems; ++iE )
2677 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2678 elemQueue->push_back( e );
2682 //=======================================================================
2684 * Split bi-quadratic elements into linear ones without creation of additional nodes
2685 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2686 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2687 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2688 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2689 * will be split in order to keep the mesh conformal.
2690 * \param elems - elements to split
2692 //=======================================================================
2694 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2696 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2697 vector<const SMDS_MeshElement* > splitElems;
2698 list< const SMDS_MeshElement* > elemQueue;
2699 list< const SMDS_MeshElement* >::iterator elemIt;
2701 SMESHDS_Mesh * mesh = GetMeshDS();
2702 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2703 int nbElems, nbNodes;
2705 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2706 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2709 elemQueue.push_back( *elemSetIt );
2710 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2712 const SMDS_MeshElement* elem = *elemIt;
2713 switch( elem->GetEntityType() )
2715 case SMDSEntity_TriQuad_Hexa: // HEX27
2717 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2718 nbElems = nbNodes = 8;
2719 elemType = & hexaType;
2721 // get nodes for new elements
2722 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2723 { 1,9,20,8, 17,22,26,21 },
2724 { 2,10,20,9, 18,23,26,22 },
2725 { 3,11,20,10, 19,24,26,23 },
2726 { 16,21,26,24, 4,12,25,15 },
2727 { 17,22,26,21, 5,13,25,12 },
2728 { 18,23,26,22, 6,14,25,13 },
2729 { 19,24,26,23, 7,15,25,14 }};
2730 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2732 // add boundary faces to elemQueue
2733 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2734 { 4,5,6,7, 12,13,14,15, 25 },
2735 { 0,1,5,4, 8,17,12,16, 21 },
2736 { 1,2,6,5, 9,18,13,17, 22 },
2737 { 2,3,7,6, 10,19,14,18, 23 },
2738 { 3,0,4,7, 11,16,15,19, 24 }};
2739 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2741 // add boundary segments to elemQueue
2742 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2743 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2744 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2745 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2748 case SMDSEntity_BiQuad_Triangle: // TRIA7
2750 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2753 elemType = & quadType;
2755 // get nodes for new elements
2756 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2757 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2759 // add boundary segments to elemQueue
2760 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_Quad_Edge:
2782 if ( elemIt == elemQueue.begin() )
2783 continue; // an elem is in theElems
2784 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2787 elemType = & segType;
2789 // get nodes for new elements
2790 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2791 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2795 } // switch( elem->GetEntityType() )
2797 // Create new elements
2799 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2803 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2804 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2805 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2806 //elemType->SetID( -1 );
2808 for ( int iE = 0; iE < nbElems; ++iE )
2809 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2812 ReplaceElemInGroups( elem, splitElems, mesh );
2815 for ( size_t i = 0; i < splitElems.size(); ++i )
2816 subMesh->AddElement( splitElems[i] );
2821 //=======================================================================
2822 //function : AddToSameGroups
2823 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2824 //=======================================================================
2826 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2827 const SMDS_MeshElement* elemInGroups,
2828 SMESHDS_Mesh * aMesh)
2830 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831 if (!groups.empty()) {
2832 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2833 for ( ; grIt != groups.end(); grIt++ ) {
2834 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2835 if ( group && group->Contains( elemInGroups ))
2836 group->SMDSGroup().Add( elemToAdd );
2842 //=======================================================================
2843 //function : RemoveElemFromGroups
2844 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2845 //=======================================================================
2846 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2847 SMESHDS_Mesh * aMesh)
2849 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850 if (!groups.empty())
2852 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2853 for (; GrIt != groups.end(); GrIt++)
2855 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2856 if (!grp || grp->IsEmpty()) continue;
2857 grp->SMDSGroup().Remove(removeelem);
2862 //================================================================================
2864 * \brief Replace elemToRm by elemToAdd in the all groups
2866 //================================================================================
2868 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2869 const SMDS_MeshElement* elemToAdd,
2870 SMESHDS_Mesh * aMesh)
2872 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2873 if (!groups.empty()) {
2874 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2875 for ( ; grIt != groups.end(); grIt++ ) {
2876 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2877 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2878 group->SMDSGroup().Add( elemToAdd );
2883 //================================================================================
2885 * \brief Replace elemToRm by elemToAdd in the all groups
2887 //================================================================================
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2890 const vector<const SMDS_MeshElement*>& elemToAdd,
2891 SMESHDS_Mesh * aMesh)
2893 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894 if (!groups.empty())
2896 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897 for ( ; grIt != groups.end(); grIt++ ) {
2898 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2900 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2901 group->SMDSGroup().Add( elemToAdd[ i ] );
2906 //=======================================================================
2907 //function : QuadToTri
2908 //purpose : Cut quadrangles into triangles.
2909 // theCrit is used to select a diagonal to cut
2910 //=======================================================================
2912 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2913 const bool the13Diag)
2916 myLastCreatedElems.reserve( theElems.size() * 2 );
2918 SMESHDS_Mesh * aMesh = GetMeshDS();
2919 Handle(Geom_Surface) surface;
2920 SMESH_MesherHelper helper( *GetMesh() );
2922 TIDSortedElemSet::iterator itElem;
2923 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2925 const SMDS_MeshElement* elem = *itElem;
2926 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2929 if ( elem->NbNodes() == 4 ) {
2930 // retrieve element nodes
2931 const SMDS_MeshNode* aNodes [4];
2932 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2934 while ( itN->more() )
2935 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2937 int aShapeId = FindShape( elem );
2938 const SMDS_MeshElement* newElem1 = 0;
2939 const SMDS_MeshElement* newElem2 = 0;
2941 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2942 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2945 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2946 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2948 myLastCreatedElems.push_back(newElem1);
2949 myLastCreatedElems.push_back(newElem2);
2950 // put a new triangle on the same shape and add to the same groups
2953 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2954 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2956 AddToSameGroups( newElem1, elem, aMesh );
2957 AddToSameGroups( newElem2, elem, aMesh );
2958 aMesh->RemoveElement( elem );
2961 // Quadratic quadrangle
2963 else if ( elem->NbNodes() >= 8 )
2965 // get surface elem is on
2966 int aShapeId = FindShape( elem );
2967 if ( aShapeId != helper.GetSubShapeID() ) {
2971 shape = aMesh->IndexToShape( aShapeId );
2972 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2973 TopoDS_Face face = TopoDS::Face( shape );
2974 surface = BRep_Tool::Surface( face );
2975 if ( !surface.IsNull() )
2976 helper.SetSubShape( shape );
2980 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2981 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2982 for ( int i = 0; itN->more(); ++i )
2983 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2985 const SMDS_MeshNode* centrNode = aNodes[8];
2986 if ( centrNode == 0 )
2988 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2989 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2991 myLastCreatedNodes.push_back(centrNode);
2994 // create a new element
2995 const SMDS_MeshElement* newElem1 = 0;
2996 const SMDS_MeshElement* newElem2 = 0;
2998 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2999 aNodes[6], aNodes[7], centrNode );
3000 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3001 centrNode, aNodes[4], aNodes[5] );
3004 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3005 aNodes[7], aNodes[4], centrNode );
3006 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3007 centrNode, aNodes[5], aNodes[6] );
3009 myLastCreatedElems.push_back(newElem1);
3010 myLastCreatedElems.push_back(newElem2);
3011 // put a new triangle on the same shape and add to the same groups
3014 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3015 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3017 AddToSameGroups( newElem1, elem, aMesh );
3018 AddToSameGroups( newElem2, elem, aMesh );
3019 aMesh->RemoveElement( elem );
3026 //=======================================================================
3027 //function : getAngle
3029 //=======================================================================
3031 double getAngle(const SMDS_MeshElement * tr1,
3032 const SMDS_MeshElement * tr2,
3033 const SMDS_MeshNode * n1,
3034 const SMDS_MeshNode * n2)
3036 double angle = 2. * M_PI; // bad angle
3039 SMESH::Controls::TSequenceOfXYZ P1, P2;
3040 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3041 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3044 if(!tr1->IsQuadratic())
3045 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3047 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3048 if ( N1.SquareMagnitude() <= gp::Resolution() )
3050 if(!tr2->IsQuadratic())
3051 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3053 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3054 if ( N2.SquareMagnitude() <= gp::Resolution() )
3057 // find the first diagonal node n1 in the triangles:
3058 // take in account a diagonal link orientation
3059 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3060 for ( int t = 0; t < 2; t++ ) {
3061 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3062 int i = 0, iDiag = -1;
3063 while ( it->more()) {
3064 const SMDS_MeshElement *n = it->next();
3065 if ( n == n1 || n == n2 ) {
3069 if ( i - iDiag == 1 )
3070 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3079 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3082 angle = N1.Angle( N2 );
3087 // =================================================
3088 // class generating a unique ID for a pair of nodes
3089 // and able to return nodes by that ID
3090 // =================================================
3094 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3095 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3098 long GetLinkID (const SMDS_MeshNode * n1,
3099 const SMDS_MeshNode * n2) const
3101 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3104 bool GetNodes (const long theLinkID,
3105 const SMDS_MeshNode* & theNode1,
3106 const SMDS_MeshNode* & theNode2) const
3108 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3109 if ( !theNode1 ) return false;
3110 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3111 if ( !theNode2 ) return false;
3117 const SMESHDS_Mesh* myMesh;
3122 //=======================================================================
3123 //function : TriToQuad
3124 //purpose : Fuse neighbour triangles into quadrangles.
3125 // theCrit is used to select a neighbour to fuse with.
3126 // theMaxAngle is a max angle between element normals at which
3127 // fusion is still performed.
3128 //=======================================================================
3130 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3131 SMESH::Controls::NumericalFunctorPtr theCrit,
3132 const double theMaxAngle)
3135 myLastCreatedElems.reserve( theElems.size() / 2 );
3137 if ( !theCrit.get() )
3140 SMESHDS_Mesh * aMesh = GetMeshDS();
3142 // Prepare data for algo: build
3143 // 1. map of elements with their linkIDs
3144 // 2. map of linkIDs with their elements
3146 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3147 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3148 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3149 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3151 TIDSortedElemSet::iterator itElem;
3152 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3154 const SMDS_MeshElement* elem = *itElem;
3155 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3156 bool IsTria = ( elem->NbCornerNodes()==3 );
3157 if (!IsTria) continue;
3159 // retrieve element nodes
3160 const SMDS_MeshNode* aNodes [4];
3161 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3164 aNodes[ i++ ] = itN->next();
3165 aNodes[ 3 ] = aNodes[ 0 ];
3168 for ( i = 0; i < 3; i++ ) {
3169 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3170 // check if elements sharing a link can be fused
3171 itLE = mapLi_listEl.find( link );
3172 if ( itLE != mapLi_listEl.end() ) {
3173 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3175 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3176 //if ( FindShape( elem ) != FindShape( elem2 ))
3177 // continue; // do not fuse triangles laying on different shapes
3178 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3179 continue; // avoid making badly shaped quads
3180 (*itLE).second.push_back( elem );
3183 mapLi_listEl[ link ].push_back( elem );
3185 mapEl_setLi [ elem ].insert( link );
3188 // Clean the maps from the links shared by a sole element, ie
3189 // links to which only one element is bound in mapLi_listEl
3191 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3192 int nbElems = (*itLE).second.size();
3193 if ( nbElems < 2 ) {
3194 const SMDS_MeshElement* elem = (*itLE).second.front();
3195 SMESH_TLink link = (*itLE).first;
3196 mapEl_setLi[ elem ].erase( link );
3197 if ( mapEl_setLi[ elem ].empty() )
3198 mapEl_setLi.erase( elem );
3202 // Algo: fuse triangles into quadrangles
3204 while ( ! mapEl_setLi.empty() ) {
3205 // Look for the start element:
3206 // the element having the least nb of shared links
3207 const SMDS_MeshElement* startElem = 0;
3209 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3210 int nbLinks = (*itEL).second.size();
3211 if ( nbLinks < minNbLinks ) {
3212 startElem = (*itEL).first;
3213 minNbLinks = nbLinks;
3214 if ( minNbLinks == 1 )
3219 // search elements to fuse starting from startElem or links of elements
3220 // fused earlyer - startLinks
3221 list< SMESH_TLink > startLinks;
3222 while ( startElem || !startLinks.empty() ) {
3223 while ( !startElem && !startLinks.empty() ) {
3224 // Get an element to start, by a link
3225 SMESH_TLink linkId = startLinks.front();
3226 startLinks.pop_front();
3227 itLE = mapLi_listEl.find( linkId );
3228 if ( itLE != mapLi_listEl.end() ) {
3229 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3230 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3231 for ( ; itE != listElem.end() ; itE++ )
3232 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3234 mapLi_listEl.erase( itLE );
3239 // Get candidates to be fused
3240 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3241 const SMESH_TLink *link12 = 0, *link13 = 0;
3243 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3244 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3245 ASSERT( !setLi.empty() );
3246 set< SMESH_TLink >::iterator itLi;
3247 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3249 const SMESH_TLink & link = (*itLi);
3250 itLE = mapLi_listEl.find( link );
3251 if ( itLE == mapLi_listEl.end() )
3254 const SMDS_MeshElement* elem = (*itLE).second.front();
3256 elem = (*itLE).second.back();
3257 mapLi_listEl.erase( itLE );
3258 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3269 // add other links of elem to list of links to re-start from
3270 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3271 set< SMESH_TLink >::iterator it;
3272 for ( it = links.begin(); it != links.end(); it++ ) {
3273 const SMESH_TLink& link2 = (*it);
3274 if ( link2 != link )
3275 startLinks.push_back( link2 );
3279 // Get nodes of possible quadrangles
3280 const SMDS_MeshNode *n12 [4], *n13 [4];
3281 bool Ok12 = false, Ok13 = false;
3282 const SMDS_MeshNode *linkNode1, *linkNode2;
3284 linkNode1 = link12->first;
3285 linkNode2 = link12->second;
3286 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3290 linkNode1 = link13->first;
3291 linkNode2 = link13->second;
3292 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3296 // Choose a pair to fuse
3297 if ( Ok12 && Ok13 ) {
3298 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3299 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3300 double aBadRate12 = getBadRate( &quad12, theCrit );
3301 double aBadRate13 = getBadRate( &quad13, theCrit );
3302 if ( aBadRate13 < aBadRate12 )
3309 // and remove fused elems and remove links from the maps
3310 mapEl_setLi.erase( tr1 );
3313 mapEl_setLi.erase( tr2 );
3314 mapLi_listEl.erase( *link12 );
3315 if ( tr1->NbNodes() == 3 )
3317 const SMDS_MeshElement* newElem = 0;
3318 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3319 myLastCreatedElems.push_back(newElem);
3320 AddToSameGroups( newElem, tr1, aMesh );
3321 int aShapeId = tr1->getshapeId();
3323 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3324 aMesh->RemoveElement( tr1 );
3325 aMesh->RemoveElement( tr2 );
3328 vector< const SMDS_MeshNode* > N1;
3329 vector< const SMDS_MeshNode* > N2;
3330 getNodesFromTwoTria(tr1,tr2,N1,N2);
3331 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3332 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3333 // i.e. first nodes from both arrays form a new diagonal
3334 const SMDS_MeshNode* aNodes[8];
3343 const SMDS_MeshElement* newElem = 0;
3344 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3345 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3346 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3348 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3350 myLastCreatedElems.push_back(newElem);
3351 AddToSameGroups( newElem, tr1, aMesh );
3352 int aShapeId = tr1->getshapeId();
3354 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3355 aMesh->RemoveElement( tr1 );
3356 aMesh->RemoveElement( tr2 );
3357 // remove middle node (9)
3358 if ( N1[4]->NbInverseElements() == 0 )
3359 aMesh->RemoveNode( N1[4] );
3360 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3361 aMesh->RemoveNode( N1[6] );
3362 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3363 aMesh->RemoveNode( N2[6] );
3368 mapEl_setLi.erase( tr3 );
3369 mapLi_listEl.erase( *link13 );
3370 if ( tr1->NbNodes() == 3 ) {
3371 const SMDS_MeshElement* newElem = 0;
3372 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3373 myLastCreatedElems.push_back(newElem);
3374 AddToSameGroups( newElem, tr1, aMesh );
3375 int aShapeId = tr1->getshapeId();
3377 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378 aMesh->RemoveElement( tr1 );
3379 aMesh->RemoveElement( tr3 );
3382 vector< const SMDS_MeshNode* > N1;
3383 vector< const SMDS_MeshNode* > N2;
3384 getNodesFromTwoTria(tr1,tr3,N1,N2);
3385 // now we receive following N1 and N2 (using numeration as above image)
3386 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3387 // i.e. first nodes from both arrays form a new diagonal
3388 const SMDS_MeshNode* aNodes[8];
3397 const SMDS_MeshElement* newElem = 0;
3398 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3399 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3400 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3402 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3404 myLastCreatedElems.push_back(newElem);
3405 AddToSameGroups( newElem, tr1, aMesh );
3406 int aShapeId = tr1->getshapeId();
3408 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3409 aMesh->RemoveElement( tr1 );
3410 aMesh->RemoveElement( tr3 );
3411 // remove middle node (9)
3412 if ( N1[4]->NbInverseElements() == 0 )
3413 aMesh->RemoveNode( N1[4] );
3414 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3415 aMesh->RemoveNode( N1[6] );
3416 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3417 aMesh->RemoveNode( N2[6] );
3421 // Next element to fuse: the rejected one
3423 startElem = Ok12 ? tr3 : tr2;
3425 } // if ( startElem )
3426 } // while ( startElem || !startLinks.empty() )
3427 } // while ( ! mapEl_setLi.empty() )
3432 //================================================================================
3434 * \brief Return nodes linked to the given one
3435 * \param theNode - the node
3436 * \param linkedNodes - the found nodes
3437 * \param type - the type of elements to check
3439 * Medium nodes are ignored
3441 //================================================================================
3443 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3444 TIDSortedElemSet & linkedNodes,
3445 SMDSAbs_ElementType type )
3447 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3448 while ( elemIt->more() )
3450 const SMDS_MeshElement* elem = elemIt->next();
3451 if(elem->GetType() == SMDSAbs_0DElement)
3454 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3455 if ( elem->GetType() == SMDSAbs_Volume )
3457 SMDS_VolumeTool vol( elem );
3458 while ( nodeIt->more() ) {
3459 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3460 if ( theNode != n && vol.IsLinked( theNode, n ))
3461 linkedNodes.insert( n );
3466 for ( int i = 0; nodeIt->more(); ++i ) {
3467 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468 if ( n == theNode ) {
3469 int iBefore = i - 1;
3471 if ( elem->IsQuadratic() ) {
3472 int nb = elem->NbNodes() / 2;
3473 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3474 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3476 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3477 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3484 //=======================================================================
3485 //function : laplacianSmooth
3486 //purpose : pulls theNode toward the center of surrounding nodes directly
3487 // connected to that node along an element edge
3488 //=======================================================================
3490 void laplacianSmooth(const SMDS_MeshNode* theNode,
3491 const Handle(Geom_Surface)& theSurface,
3492 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3494 // find surrounding nodes
3496 TIDSortedElemSet nodeSet;
3497 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3499 // compute new coodrs
3501 double coord[] = { 0., 0., 0. };
3502 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3503 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3504 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3505 if ( theSurface.IsNull() ) { // smooth in 3D
3506 coord[0] += node->X();
3507 coord[1] += node->Y();
3508 coord[2] += node->Z();
3510 else { // smooth in 2D
3511 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3512 gp_XY* uv = theUVMap[ node ];
3513 coord[0] += uv->X();
3514 coord[1] += uv->Y();
3517 int nbNodes = nodeSet.size();
3520 coord[0] /= nbNodes;
3521 coord[1] /= nbNodes;
3523 if ( !theSurface.IsNull() ) {
3524 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3525 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3526 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3532 coord[2] /= nbNodes;
3536 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3539 //=======================================================================
3540 //function : centroidalSmooth
3541 //purpose : pulls theNode toward the element-area-weighted centroid of the
3542 // surrounding elements
3543 //=======================================================================
3545 void centroidalSmooth(const SMDS_MeshNode* theNode,
3546 const Handle(Geom_Surface)& theSurface,
3547 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3549 gp_XYZ aNewXYZ(0.,0.,0.);
3550 SMESH::Controls::Area anAreaFunc;
3551 double totalArea = 0.;
3556 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3557 while ( elemIt->more() )
3559 const SMDS_MeshElement* elem = elemIt->next();
3562 gp_XYZ elemCenter(0.,0.,0.);
3563 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3564 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3565 int nn = elem->NbNodes();
3566 if(elem->IsQuadratic()) nn = nn/2;
3568 //while ( itN->more() ) {
3570 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3572 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3573 aNodePoints.push_back( aP );
3574 if ( !theSurface.IsNull() ) { // smooth in 2D
3575 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3576 gp_XY* uv = theUVMap[ aNode ];
3577 aP.SetCoord( uv->X(), uv->Y(), 0. );
3581 double elemArea = anAreaFunc.GetValue( aNodePoints );
3582 totalArea += elemArea;
3584 aNewXYZ += elemCenter * elemArea;
3586 aNewXYZ /= totalArea;
3587 if ( !theSurface.IsNull() ) {
3588 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3589 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3594 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3597 //=======================================================================
3598 //function : getClosestUV
3599 //purpose : return UV of closest projection
3600 //=======================================================================
3602 static bool getClosestUV (Extrema_GenExtPS& projector,
3603 const gp_Pnt& point,
3606 projector.Perform( point );
3607 if ( projector.IsDone() ) {
3608 double u, v, minVal = DBL_MAX;
3609 for ( int i = projector.NbExt(); i > 0; i-- )
3610 if ( projector.SquareDistance( i ) < minVal ) {
3611 minVal = projector.SquareDistance( i );
3612 projector.Point( i ).Parameter( u, v );
3614 result.SetCoord( u, v );
3620 //=======================================================================
3622 //purpose : Smooth theElements during theNbIterations or until a worst
3623 // element has aspect ratio <= theTgtAspectRatio.
3624 // Aspect Ratio varies in range [1.0, inf].
3625 // If theElements is empty, the whole mesh is smoothed.
3626 // theFixedNodes contains additionally fixed nodes. Nodes built
3627 // on edges and boundary nodes are always fixed.
3628 //=======================================================================
3630 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3631 set<const SMDS_MeshNode*> & theFixedNodes,
3632 const SmoothMethod theSmoothMethod,
3633 const int theNbIterations,
3634 double theTgtAspectRatio,
3639 if ( theTgtAspectRatio < 1.0 )
3640 theTgtAspectRatio = 1.0;
3642 const double disttol = 1.e-16;
3644 SMESH::Controls::AspectRatio aQualityFunc;
3646 SMESHDS_Mesh* aMesh = GetMeshDS();
3648 if ( theElems.empty() ) {
3649 // add all faces to theElems
3650 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3651 while ( fIt->more() ) {
3652 const SMDS_MeshElement* face = fIt->next();
3653 theElems.insert( theElems.end(), face );
3656 // get all face ids theElems are on
3657 set< int > faceIdSet;
3658 TIDSortedElemSet::iterator itElem;
3660 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3661 int fId = FindShape( *itElem );
3662 // check that corresponding submesh exists and a shape is face
3664 faceIdSet.find( fId ) == faceIdSet.end() &&
3665 aMesh->MeshElements( fId )) {
3666 TopoDS_Shape F = aMesh->IndexToShape( fId );
3667 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3668 faceIdSet.insert( fId );
3671 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3673 // ===============================================
3674 // smooth elements on each TopoDS_Face separately
3675 // ===============================================
3677 SMESH_MesherHelper helper( *GetMesh() );
3679 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3680 for ( ; fId != faceIdSet.rend(); ++fId )
3682 // get face surface and submesh
3683 Handle(Geom_Surface) surface;
3684 SMESHDS_SubMesh* faceSubMesh = 0;
3687 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3688 bool isUPeriodic = false, isVPeriodic = false;
3691 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3692 surface = BRep_Tool::Surface( face );
3693 faceSubMesh = aMesh->MeshElements( *fId );
3694 fToler2 = BRep_Tool::Tolerance( face );
3695 fToler2 *= fToler2 * 10.;
3696 isUPeriodic = surface->IsUPeriodic();
3697 // if ( isUPeriodic )
3698 // surface->UPeriod();
3699 isVPeriodic = surface->IsVPeriodic();
3700 // if ( isVPeriodic )
3701 // surface->VPeriod();
3702 surface->Bounds( u1, u2, v1, v2 );
3703 helper.SetSubShape( face );
3705 // ---------------------------------------------------------
3706 // for elements on a face, find movable and fixed nodes and
3707 // compute UV for them
3708 // ---------------------------------------------------------
3709 bool checkBoundaryNodes = false;
3710 bool isQuadratic = false;
3711 set<const SMDS_MeshNode*> setMovableNodes;
3712 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3713 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3714 list< const SMDS_MeshElement* > elemsOnFace;
3716 Extrema_GenExtPS projector;
3717 GeomAdaptor_Surface surfAdaptor;
3718 if ( !surface.IsNull() ) {
3719 surfAdaptor.Load( surface );
3720 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3722 int nbElemOnFace = 0;
3723 itElem = theElems.begin();
3724 // loop on not yet smoothed elements: look for elems on a face
3725 while ( itElem != theElems.end() )
3727 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3728 break; // all elements found
3730 const SMDS_MeshElement* elem = *itElem;
3731 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3732 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3736 elemsOnFace.push_back( elem );
3737 theElems.erase( itElem++ );
3741 isQuadratic = elem->IsQuadratic();
3743 // get movable nodes of elem
3744 const SMDS_MeshNode* node;
3745 SMDS_TypeOfPosition posType;
3746 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3747 int nn = 0, nbn = elem->NbNodes();
3748 if(elem->IsQuadratic())
3750 while ( nn++ < nbn ) {
3751 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3752 const SMDS_PositionPtr& pos = node->GetPosition();
3753 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3754 if (posType != SMDS_TOP_EDGE &&
3755 posType != SMDS_TOP_VERTEX &&
3756 theFixedNodes.find( node ) == theFixedNodes.end())
3758 // check if all faces around the node are on faceSubMesh
3759 // because a node on edge may be bound to face
3761 if ( faceSubMesh ) {
3762 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3763 while ( eIt->more() && all ) {
3764 const SMDS_MeshElement* e = eIt->next();
3765 all = faceSubMesh->Contains( e );
3769 setMovableNodes.insert( node );
3771 checkBoundaryNodes = true;
3773 if ( posType == SMDS_TOP_3DSPACE )
3774 checkBoundaryNodes = true;
3777 if ( surface.IsNull() )
3780 // get nodes to check UV
3781 list< const SMDS_MeshNode* > uvCheckNodes;
3782 const SMDS_MeshNode* nodeInFace = 0;
3783 itN = elem->nodesIterator();
3784 nn = 0; nbn = elem->NbNodes();
3785 if(elem->IsQuadratic())
3787 while ( nn++ < nbn ) {
3788 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789 if ( node->GetPosition()->GetDim() == 2 )
3791 if ( uvMap.find( node ) == uvMap.end() )
3792 uvCheckNodes.push_back( node );
3793 // add nodes of elems sharing node
3794 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3795 // while ( eIt->more() ) {
3796 // const SMDS_MeshElement* e = eIt->next();
3797 // if ( e != elem ) {
3798 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3799 // while ( nIt->more() ) {
3800 // const SMDS_MeshNode* n =
3801 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3802 // if ( uvMap.find( n ) == uvMap.end() )
3803 // uvCheckNodes.push_back( n );
3809 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3810 for ( ; n != uvCheckNodes.end(); ++n ) {
3813 const SMDS_PositionPtr& pos = node->GetPosition();
3814 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3818 bool toCheck = true;
3819 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3821 // compute not existing UV
3822 bool project = ( posType == SMDS_TOP_3DSPACE );
3823 // double dist1 = DBL_MAX, dist2 = 0;
3824 // if ( posType != SMDS_TOP_3DSPACE ) {
3825 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3826 // project = dist1 > fToler2;
3828 if ( project ) { // compute new UV
3830 gp_Pnt pNode = SMESH_TNodeXYZ( node );
3831 if ( !getClosestUV( projector, pNode, newUV )) {
3832 MESSAGE("Node Projection Failed " << node);
3836 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3838 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3840 // if ( posType != SMDS_TOP_3DSPACE )
3841 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3842 // if ( dist2 < dist1 )
3846 // store UV in the map
3847 listUV.push_back( uv );
3848 uvMap.insert( make_pair( node, &listUV.back() ));
3850 } // loop on not yet smoothed elements
3852 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3853 checkBoundaryNodes = true;
3855 // fix nodes on mesh boundary
3857 if ( checkBoundaryNodes ) {
3858 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3859 map< SMESH_TLink, int >::iterator link_nb;
3860 // put all elements links to linkNbMap
3861 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3862 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3863 const SMDS_MeshElement* elem = (*elemIt);
3864 int nbn = elem->NbCornerNodes();
3865 // loop on elem links: insert them in linkNbMap
3866 for ( int iN = 0; iN < nbn; ++iN ) {
3867 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3868 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3869 SMESH_TLink link( n1, n2 );
3870 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3874 // remove nodes that are in links encountered only once from setMovableNodes
3875 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3876 if ( link_nb->second == 1 ) {
3877 setMovableNodes.erase( link_nb->first.node1() );
3878 setMovableNodes.erase( link_nb->first.node2() );
3883 // -----------------------------------------------------
3884 // for nodes on seam edge, compute one more UV ( uvMap2 );
3885 // find movable nodes linked to nodes on seam and which
3886 // are to be smoothed using the second UV ( uvMap2 )
3887 // -----------------------------------------------------
3889 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3890 if ( !surface.IsNull() ) {
3891 TopExp_Explorer eExp( face, TopAbs_EDGE );
3892 for ( ; eExp.More(); eExp.Next() ) {
3893 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3894 if ( !BRep_Tool::IsClosed( edge, face ))
3896 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3897 if ( !sm ) continue;
3898 // find out which parameter varies for a node on seam
3901 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3902 if ( pcurve.IsNull() ) continue;
3903 uv1 = pcurve->Value( f );
3905 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3906 if ( pcurve.IsNull() ) continue;
3907 uv2 = pcurve->Value( f );
3908 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3910 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3911 std::swap( uv1, uv2 );
3912 // get nodes on seam and its vertices
3913 list< const SMDS_MeshNode* > seamNodes;
3914 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3915 while ( nSeamIt->more() ) {
3916 const SMDS_MeshNode* node = nSeamIt->next();
3917 if ( !isQuadratic || !IsMedium( node ))
3918 seamNodes.push_back( node );
3920 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3921 for ( ; vExp.More(); vExp.Next() ) {
3922 sm = aMesh->MeshElements( vExp.Current() );
3924 nSeamIt = sm->GetNodes();
3925 while ( nSeamIt->more() )
3926 seamNodes.push_back( nSeamIt->next() );
3929 // loop on nodes on seam
3930 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3931 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3932 const SMDS_MeshNode* nSeam = *noSeIt;
3933 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3934 if ( n_uv == uvMap.end() )
3937 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3938 // set the second UV
3939 listUV.push_back( *n_uv->second );
3940 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3941 if ( uvMap2.empty() )
3942 uvMap2 = uvMap; // copy the uvMap contents
3943 uvMap2[ nSeam ] = &listUV.back();
3945 // collect movable nodes linked to ones on seam in nodesNearSeam
3946 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3947 while ( eIt->more() ) {
3948 const SMDS_MeshElement* e = eIt->next();
3949 int nbUseMap1 = 0, nbUseMap2 = 0;
3950 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3951 int nn = 0, nbn = e->NbNodes();
3952 if(e->IsQuadratic()) nbn = nbn/2;
3953 while ( nn++ < nbn )
3955 const SMDS_MeshNode* n =
3956 static_cast<const SMDS_MeshNode*>( nIt->next() );
3958 setMovableNodes.find( n ) == setMovableNodes.end() )
3960 // add only nodes being closer to uv2 than to uv1
3961 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3962 // 0.5 * ( n->Y() + nSeam->Y() ),
3963 // 0.5 * ( n->Z() + nSeam->Z() ));
3965 // getClosestUV( projector, pMid, uv );
3966 double x = uvMap[ n ]->Coord( iPar );
3967 if ( Abs( uv1.Coord( iPar ) - x ) >
3968 Abs( uv2.Coord( iPar ) - x )) {
3969 nodesNearSeam.insert( n );
3975 // for centroidalSmooth all element nodes must
3976 // be on one side of a seam
3977 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3978 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3980 while ( nn++ < nbn ) {
3981 const SMDS_MeshNode* n =
3982 static_cast<const SMDS_MeshNode*>( nIt->next() );
3983 setMovableNodes.erase( n );
3987 } // loop on nodes on seam
3988 } // loop on edge of a face
3989 } // if ( !face.IsNull() )
3991 if ( setMovableNodes.empty() ) {
3992 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3993 continue; // goto next face
4001 double maxRatio = -1., maxDisplacement = -1.;
4002 set<const SMDS_MeshNode*>::iterator nodeToMove;
4003 for ( it = 0; it < theNbIterations; it++ ) {
4004 maxDisplacement = 0.;
4005 nodeToMove = setMovableNodes.begin();
4006 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4007 const SMDS_MeshNode* node = (*nodeToMove);
4008 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4011 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4012 if ( theSmoothMethod == LAPLACIAN )
4013 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017 // node displacement
4018 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4019 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4020 if ( aDispl > maxDisplacement )
4021 maxDisplacement = aDispl;
4023 // no node movement => exit
4024 //if ( maxDisplacement < 1.e-16 ) {
4025 if ( maxDisplacement < disttol ) {
4026 MESSAGE("-- no node movement --");
4030 // check elements quality
4032 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4033 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4034 const SMDS_MeshElement* elem = (*elemIt);
4035 if ( !elem || elem->GetType() != SMDSAbs_Face )
4037 SMESH::Controls::TSequenceOfXYZ aPoints;
4038 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4039 double aValue = aQualityFunc.GetValue( aPoints );
4040 if ( aValue > maxRatio )
4044 if ( maxRatio <= theTgtAspectRatio ) {
4045 MESSAGE("-- quality achived --");
4048 if (it+1 == theNbIterations) {
4049 MESSAGE("-- Iteration limit exceeded --");
4051 } // smoothing iterations
4053 MESSAGE(" Face id: " << *fId <<
4054 " Nb iterstions: " << it <<
4055 " Displacement: " << maxDisplacement <<
4056 " Aspect Ratio " << maxRatio);
4058 // ---------------------------------------
4059 // new nodes positions are computed,
4060 // record movement in DS and set new UV
4061 // ---------------------------------------
4062 nodeToMove = setMovableNodes.begin();
4063 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4064 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4065 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4066 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4067 if ( node_uv != uvMap.end() ) {
4068 gp_XY* uv = node_uv->second;
4070 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4074 // move medium nodes of quadratic elements
4077 vector<const SMDS_MeshNode*> nodes;
4079 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4080 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4082 const SMDS_MeshElement* QF = *elemIt;
4083 if ( QF->IsQuadratic() )
4085 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4086 SMDS_MeshElement::iterator() );
4087 nodes.push_back( nodes[0] );
4089 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4091 if ( !surface.IsNull() )
4093 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4094 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4095 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4096 xyz = surface->Value( uv.X(), uv.Y() );
4099 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4101 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4102 // we have to move a medium node
4103 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4109 } // loop on face ids
4115 //=======================================================================
4116 //function : isReverse
4117 //purpose : Return true if normal of prevNodes is not co-directied with
4118 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4119 // iNotSame is where prevNodes and nextNodes are different.
4120 // If result is true then future volume orientation is OK
4121 //=======================================================================
4123 bool isReverse(const SMDS_MeshElement* face,
4124 const vector<const SMDS_MeshNode*>& prevNodes,
4125 const vector<const SMDS_MeshNode*>& nextNodes,
4129 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4130 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4131 gp_XYZ extrDir( pN - pP ), faceNorm;
4132 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4134 return faceNorm * extrDir < 0.0;
4137 //================================================================================
4139 * \brief Assure that theElemSets[0] holds elements, not nodes
4141 //================================================================================
4143 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4145 if ( !theElemSets[0].empty() &&
4146 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4148 std::swap( theElemSets[0], theElemSets[1] );
4150 else if ( !theElemSets[1].empty() &&
4151 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4153 std::swap( theElemSets[0], theElemSets[1] );
4158 //=======================================================================
4160 * \brief Create elements by sweeping an element
4161 * \param elem - element to sweep
4162 * \param newNodesItVec - nodes generated from each node of the element
4163 * \param newElems - generated elements
4164 * \param nbSteps - number of sweeping steps
4165 * \param srcElements - to append elem for each generated element
4167 //=======================================================================
4169 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4170 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4171 list<const SMDS_MeshElement*>& newElems,
4172 const size_t nbSteps,
4173 SMESH_SequenceOfElemPtr& srcElements)
4175 SMESHDS_Mesh* aMesh = GetMeshDS();
4177 const int nbNodes = elem->NbNodes();
4178 const int nbCorners = elem->NbCornerNodes();
4179 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4180 polyhedron creation !!! */
4181 // Loop on elem nodes:
4182 // find new nodes and detect same nodes indices
4183 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4184 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4185 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4186 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4188 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4189 vector<int> sames(nbNodes);
4190 vector<bool> isSingleNode(nbNodes);
4192 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4194 const SMDS_MeshNode* node = nnIt->first;
4195 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4196 if ( listNewNodes.empty() )
4199 itNN [ iNode ] = listNewNodes.begin();
4200 prevNod[ iNode ] = node;
4201 nextNod[ iNode ] = listNewNodes.front();
4203 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4204 corner node of linear */
4205 if ( prevNod[ iNode ] != nextNod [ iNode ])
4206 nbDouble += !isSingleNode[iNode];
4208 if( iNode < nbCorners ) { // check corners only
4209 if ( prevNod[ iNode ] == nextNod [ iNode ])
4210 sames[nbSame++] = iNode;
4212 iNotSameNode = iNode;
4216 if ( nbSame == nbNodes || nbSame > 2) {
4217 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4221 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4223 // fix nodes order to have bottom normal external
4224 if ( baseType == SMDSEntity_Polygon )
4226 std::reverse( itNN.begin(), itNN.end() );
4227 std::reverse( prevNod.begin(), prevNod.end() );
4228 std::reverse( midlNod.begin(), midlNod.end() );
4229 std::reverse( nextNod.begin(), nextNod.end() );
4230 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4234 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4235 SMDS_MeshCell::applyInterlace( ind, itNN );
4236 SMDS_MeshCell::applyInterlace( ind, prevNod );
4237 SMDS_MeshCell::applyInterlace( ind, nextNod );
4238 SMDS_MeshCell::applyInterlace( ind, midlNod );
4239 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4242 sames[nbSame] = iNotSameNode;
4243 for ( int j = 0; j <= nbSame; ++j )
4244 for ( size_t i = 0; i < ind.size(); ++i )
4245 if ( ind[i] == sames[j] )
4250 iNotSameNode = sames[nbSame];
4254 else if ( elem->GetType() == SMDSAbs_Edge )
4256 // orient a new face same as adjacent one
4258 const SMDS_MeshElement* e;
4259 TIDSortedElemSet dummy;
4260 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4261 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4262 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4264 // there is an adjacent face, check order of nodes in it
4265 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4268 std::swap( itNN[0], itNN[1] );
4269 std::swap( prevNod[0], prevNod[1] );
4270 std::swap( nextNod[0], nextNod[1] );
4271 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4273 sames[0] = 1 - sames[0];
4274 iNotSameNode = 1 - iNotSameNode;
4279 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4281 iSameNode = sames[ nbSame-1 ];
4282 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4283 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4284 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4287 if ( baseType == SMDSEntity_Polygon )
4289 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4290 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4292 else if ( baseType == SMDSEntity_Quad_Polygon )
4294 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4295 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4298 // make new elements
4299 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4302 for ( iNode = 0; iNode < nbNodes; iNode++ )
4304 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4305 nextNod[ iNode ] = *itNN[ iNode ]++;
4308 SMDS_MeshElement* aNewElem = 0;
4309 /*if(!elem->IsPoly())*/ {
4310 switch ( baseType ) {
4312 case SMDSEntity_Node: { // sweep NODE
4313 if ( nbSame == 0 ) {
4314 if ( isSingleNode[0] )
4315 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4317 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4323 case SMDSEntity_Edge: { // sweep EDGE
4324 if ( nbDouble == 0 )
4326 if ( nbSame == 0 ) // ---> quadrangle
4327 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4328 nextNod[ 1 ], nextNod[ 0 ] );
4329 else // ---> triangle
4330 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331 nextNod[ iNotSameNode ] );
4333 else // ---> polygon
4335 vector<const SMDS_MeshNode*> poly_nodes;
4336 poly_nodes.push_back( prevNod[0] );
4337 poly_nodes.push_back( prevNod[1] );
4338 if ( prevNod[1] != nextNod[1] )
4340 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4341 poly_nodes.push_back( nextNod[1] );
4343 if ( prevNod[0] != nextNod[0] )
4345 poly_nodes.push_back( nextNod[0] );
4346 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4348 switch ( poly_nodes.size() ) {
4350 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4353 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4354 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4357 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4362 case SMDSEntity_Triangle: // TRIANGLE --->
4364 if ( nbDouble > 0 ) break;
4365 if ( nbSame == 0 ) // ---> pentahedron
4366 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4367 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4369 else if ( nbSame == 1 ) // ---> pyramid
4370 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4371 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4372 nextNod[ iSameNode ]);
4374 else // 2 same nodes: ---> tetrahedron
4375 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4376 nextNod[ iNotSameNode ]);
4379 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4383 if ( nbDouble+nbSame == 2 )
4385 if(nbSame==0) { // ---> quadratic quadrangle
4386 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4387 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4389 else { //(nbSame==1) // ---> quadratic triangle
4391 return; // medium node on axis
4393 else if(sames[0]==0)
4394 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4395 prevNod[2], midlNod[1], nextNod[2] );
4397 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4398 prevNod[2], nextNod[2], midlNod[0]);
4401 else if ( nbDouble == 3 )
4403 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4404 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4405 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4412 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4413 if ( nbDouble > 0 ) break;
4415 if ( nbSame == 0 ) // ---> hexahedron
4416 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4417 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4419 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4420 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4421 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4422 nextNod[ iSameNode ]);
4423 newElems.push_back( aNewElem );
4424 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4425 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4426 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4428 else if ( nbSame == 2 ) { // ---> pentahedron
4429 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4430 // iBeforeSame is same too
4431 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4432 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4433 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4435 // iAfterSame is same too
4436 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4437 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4438 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4442 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4443 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4444 if ( nbDouble+nbSame != 3 ) break;
4446 // ---> pentahedron with 15 nodes
4447 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4448 nextNod[0], nextNod[1], nextNod[2],
4449 prevNod[3], prevNod[4], prevNod[5],
4450 nextNod[3], nextNod[4], nextNod[5],
4451 midlNod[0], midlNod[1], midlNod[2]);
4453 else if(nbSame==1) {
4454 // ---> 2d order pyramid of 13 nodes
4455 int apex = iSameNode;
4456 int i0 = ( apex + 1 ) % nbCorners;
4457 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4461 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4462 nextNod[i0], nextNod[i1], prevNod[apex],
4463 prevNod[i01], midlNod[i0],
4464 nextNod[i01], midlNod[i1],
4465 prevNod[i1a], prevNod[i0a],
4466 nextNod[i0a], nextNod[i1a]);
4468 else if(nbSame==2) {
4469 // ---> 2d order tetrahedron of 10 nodes
4470 int n1 = iNotSameNode;
4471 int n2 = ( n1 + 1 ) % nbCorners;
4472 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4476 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4477 prevNod[n12], prevNod[n23], prevNod[n31],
4478 midlNod[n1], nextNod[n12], nextNod[n31]);
4482 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4484 if ( nbDouble != 4 ) break;
4485 // ---> hexahedron with 20 nodes
4486 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4487 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4488 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4489 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4490 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4492 else if(nbSame==1) {
4493 // ---> pyramid + pentahedron - can not be created since it is needed
4494 // additional middle node at the center of face
4495 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4498 else if( nbSame == 2 ) {
4499 if ( nbDouble != 2 ) break;
4500 // ---> 2d order Pentahedron with 15 nodes
4502 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4503 // iBeforeSame is same too
4510 // iAfterSame is same too
4520 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4521 prevNod[n4], prevNod[n5], nextNod[n5],
4522 prevNod[n12], midlNod[n2], nextNod[n12],
4523 prevNod[n45], midlNod[n5], nextNod[n45],
4524 prevNod[n14], prevNod[n25], nextNod[n25]);
4528 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4530 if( nbSame == 0 && nbDouble == 9 ) {
4531 // ---> tri-quadratic hexahedron with 27 nodes
4532 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4533 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4534 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4535 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4536 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4537 prevNod[8], // bottom center
4538 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4539 nextNod[8], // top center
4540 midlNod[8]);// elem center
4548 case SMDSEntity_Polygon: { // sweep POLYGON
4550 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4551 // ---> hexagonal prism
4552 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4553 prevNod[3], prevNod[4], prevNod[5],
4554 nextNod[0], nextNod[1], nextNod[2],
4555 nextNod[3], nextNod[4], nextNod[5]);
4559 case SMDSEntity_Ball:
4564 } // switch ( baseType )
4567 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4569 if ( baseType != SMDSEntity_Polygon )
4571 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4572 SMDS_MeshCell::applyInterlace( ind, prevNod );
4573 SMDS_MeshCell::applyInterlace( ind, nextNod );
4574 SMDS_MeshCell::applyInterlace( ind, midlNod );
4575 SMDS_MeshCell::applyInterlace( ind, itNN );
4576 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4577 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4579 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4580 vector<int> quantities (nbNodes + 2);
4581 polyedre_nodes.clear();
4585 for (int inode = 0; inode < nbNodes; inode++)
4586 polyedre_nodes.push_back( prevNod[inode] );
4587 quantities.push_back( nbNodes );
4590 polyedre_nodes.push_back( nextNod[0] );
4591 for (int inode = nbNodes; inode-1; --inode )
4592 polyedre_nodes.push_back( nextNod[inode-1] );
4593 quantities.push_back( nbNodes );
4601 const int iQuad = elem->IsQuadratic();
4602 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4604 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4605 int inextface = (iface+1+iQuad) % nbNodes;
4606 int imid = (iface+1) % nbNodes;
4607 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4608 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4609 polyedre_nodes.push_back( prevNod[iface] ); // 1
4610 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4612 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4613 polyedre_nodes.push_back( nextNod[iface] ); // 2
4615 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4616 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4618 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4619 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4621 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4622 if ( nbFaceNodes > 2 )
4623 quantities.push_back( nbFaceNodes );
4624 else // degenerated face
4625 polyedre_nodes.resize( prevNbNodes );
4627 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4629 } // try to create a polyherdal prism
4632 newElems.push_back( aNewElem );
4633 myLastCreatedElems.push_back(aNewElem);
4634 srcElements.push_back( elem );
4637 // set new prev nodes
4638 for ( iNode = 0; iNode < nbNodes; iNode++ )
4639 prevNod[ iNode ] = nextNod[ iNode ];
4644 //=======================================================================
4646 * \brief Create 1D and 2D elements around swept elements
4647 * \param mapNewNodes - source nodes and ones generated from them
4648 * \param newElemsMap - source elements and ones generated from them
4649 * \param elemNewNodesMap - nodes generated from each node of each element
4650 * \param elemSet - all swept elements
4651 * \param nbSteps - number of sweeping steps
4652 * \param srcElements - to append elem for each generated element
4654 //=======================================================================
4656 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4657 TTElemOfElemListMap & newElemsMap,
4658 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4659 TIDSortedElemSet& elemSet,
4661 SMESH_SequenceOfElemPtr& srcElements)
4663 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4664 SMESHDS_Mesh* aMesh = GetMeshDS();
4666 // Find nodes belonging to only one initial element - sweep them into edges.
4668 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4669 for ( ; nList != mapNewNodes.end(); nList++ )
4671 const SMDS_MeshNode* node =
4672 static_cast<const SMDS_MeshNode*>( nList->first );
4673 if ( newElemsMap.count( node ))
4674 continue; // node was extruded into edge
4675 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4676 int nbInitElems = 0;
4677 const SMDS_MeshElement* el = 0;
4678 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4679 while ( eIt->more() && nbInitElems < 2 ) {
4680 const SMDS_MeshElement* e = eIt->next();
4681 SMDSAbs_ElementType type = e->GetType();
4682 if ( type == SMDSAbs_Volume ||
4686 if ( type > highType ) {
4693 if ( nbInitElems == 1 ) {
4694 bool NotCreateEdge = el && el->IsMediumNode(node);
4695 if(!NotCreateEdge) {
4696 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4697 list<const SMDS_MeshElement*> newEdges;
4698 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4703 // Make a ceiling for each element ie an equal element of last new nodes.
4704 // Find free links of faces - make edges and sweep them into faces.
4706 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4708 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4709 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4710 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4712 const SMDS_MeshElement* elem = itElem->first;
4713 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4715 if(itElem->second.size()==0) continue;
4717 const bool isQuadratic = elem->IsQuadratic();
4719 if ( elem->GetType() == SMDSAbs_Edge ) {
4720 // create a ceiling edge
4721 if ( !isQuadratic ) {
4722 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4723 vecNewNodes[ 1 ]->second.back())) {
4724 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4725 vecNewNodes[ 1 ]->second.back()));
4726 srcElements.push_back( elem );
4730 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4731 vecNewNodes[ 1 ]->second.back(),
4732 vecNewNodes[ 2 ]->second.back())) {
4733 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4734 vecNewNodes[ 1 ]->second.back(),
4735 vecNewNodes[ 2 ]->second.back()));
4736 srcElements.push_back( elem );
4740 if ( elem->GetType() != SMDSAbs_Face )
4743 bool hasFreeLinks = false;
4745 TIDSortedElemSet avoidSet;
4746 avoidSet.insert( elem );
4748 set<const SMDS_MeshNode*> aFaceLastNodes;
4749 int iNode, nbNodes = vecNewNodes.size();
4750 if ( !isQuadratic ) {
4751 // loop on the face nodes
4752 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4753 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4754 // look for free links of the face
4755 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4756 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4757 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4758 // check if a link n1-n2 is free
4759 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4760 hasFreeLinks = true;
4761 // make a new edge and a ceiling for a new edge
4762 const SMDS_MeshElement* edge;
4763 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4764 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4765 srcElements.push_back( myLastCreatedElems.back() );
4767 n1 = vecNewNodes[ iNode ]->second.back();
4768 n2 = vecNewNodes[ iNext ]->second.back();
4769 if ( !aMesh->FindEdge( n1, n2 )) {
4770 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4771 srcElements.push_back( edge );
4776 else { // elem is quadratic face
4777 int nbn = nbNodes/2;
4778 for ( iNode = 0; iNode < nbn; iNode++ ) {
4779 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4780 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4781 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4782 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4783 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4784 // check if a link is free
4785 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4786 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4787 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4788 hasFreeLinks = true;
4789 // make an edge and a ceiling for a new edge
4791 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4792 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4793 srcElements.push_back( elem );
4795 n1 = vecNewNodes[ iNode ]->second.back();
4796 n2 = vecNewNodes[ iNext ]->second.back();
4797 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4798 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4799 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4800 srcElements.push_back( elem );
4804 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4805 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4809 // sweep free links into faces
4811 if ( hasFreeLinks ) {
4812 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4813 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4815 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4816 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4817 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4818 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4819 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4821 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4822 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4823 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4825 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4826 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4827 std::advance( v, volNb );
4828 // find indices of free faces of a volume and their source edges
4829 list< int > freeInd;
4830 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4831 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4832 int iF, nbF = vTool.NbFaces();
4833 for ( iF = 0; iF < nbF; iF ++ ) {
4834 if (vTool.IsFreeFace( iF ) &&
4835 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4836 initNodeSet != faceNodeSet) // except an initial face
4838 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4840 if ( faceNodeSet == initNodeSetNoCenter )
4842 freeInd.push_back( iF );
4843 // find source edge of a free face iF
4844 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4845 vector<const SMDS_MeshNode*>::iterator lastCommom;
4846 commonNodes.resize( nbNodes, 0 );
4847 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4848 initNodeSet.begin(), initNodeSet.end(),
4849 commonNodes.begin());
4850 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4851 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4853 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4855 if ( !srcEdges.back() )
4857 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4858 << iF << " of volume #" << vTool.ID() << endl;
4863 if ( freeInd.empty() )
4866 // create wall faces for all steps;
4867 // if such a face has been already created by sweep of edge,
4868 // assure that its orientation is OK
4869 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4871 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4872 vTool.SetExternalNormal();
4873 const int nextShift = vTool.IsForward() ? +1 : -1;
4874 list< int >::iterator ind = freeInd.begin();
4875 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4876 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4878 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4879 int nbn = vTool.NbFaceNodes( *ind );
4880 const SMDS_MeshElement * f = 0;
4881 if ( nbn == 3 ) ///// triangle
4883 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4885 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4887 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4889 nodes[ 1 + nextShift ] };
4891 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4893 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4897 else if ( nbn == 4 ) ///// quadrangle
4899 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4901 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4903 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4904 nodes[ 2 ], nodes[ 2+nextShift ] };
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4909 newOrder[ 2 ], newOrder[ 3 ]));
4912 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4914 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4916 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4918 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4920 nodes[2 + 2*nextShift],
4921 nodes[3 - 2*nextShift],
4923 nodes[3 + 2*nextShift]};
4925 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4935 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4937 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4938 nodes[1], nodes[3], nodes[5], nodes[7] );
4940 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4942 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4943 nodes[4 - 2*nextShift],
4945 nodes[4 + 2*nextShift],
4947 nodes[5 - 2*nextShift],
4949 nodes[5 + 2*nextShift] };
4951 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4954 newOrder[ 2 ], newOrder[ 3 ],
4955 newOrder[ 4 ], newOrder[ 5 ],
4956 newOrder[ 6 ], newOrder[ 7 ]));
4959 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4961 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4962 SMDSAbs_Face, /*noMedium=*/false);
4964 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4967 nodes[4 - 2*nextShift],
4969 nodes[4 + 2*nextShift],
4971 nodes[5 - 2*nextShift],
4973 nodes[5 + 2*nextShift],
4976 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4978 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4979 newOrder[ 2 ], newOrder[ 3 ],
4980 newOrder[ 4 ], newOrder[ 5 ],
4981 newOrder[ 6 ], newOrder[ 7 ],
4985 else //////// polygon
4987 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4988 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4990 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4992 if ( !vTool.IsForward() )
4993 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4995 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4997 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5001 while ( srcElements.size() < myLastCreatedElems.size() )
5002 srcElements.push_back( *srcEdge );
5004 } // loop on free faces
5006 // go to the next volume
5008 while ( iVol++ < nbVolumesByStep ) v++;
5011 } // loop on volumes of one step
5012 } // sweep free links into faces
5014 // Make a ceiling face with a normal external to a volume
5016 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5017 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5018 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5020 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5021 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5022 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5026 lastVol.SetExternalNormal();
5027 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5028 const int nbn = lastVol.NbFaceNodes( iF );
5029 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5030 if ( !hasFreeLinks ||
5031 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5033 const vector<int>& interlace =
5034 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5035 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5037 AddElement( nodeVec, anyFace.Init( elem ));
5039 while ( srcElements.size() < myLastCreatedElems.size() )
5040 srcElements.push_back( elem );
5043 } // loop on swept elements
5046 //=======================================================================
5047 //function : RotationSweep
5049 //=======================================================================
5051 SMESH_MeshEditor::PGroupIDs
5052 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5053 const gp_Ax1& theAxis,
5054 const double theAngle,
5055 const int theNbSteps,
5056 const double theTol,
5057 const bool theMakeGroups,
5058 const bool theMakeWalls)
5062 setElemsFirst( theElemSets );
5063 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5064 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5066 // source elements for each generated one
5067 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5068 srcElems.reserve( theElemSets[0].size() );
5069 srcNodes.reserve( theElemSets[1].size() );
5072 aTrsf.SetRotation( theAxis, theAngle );
5074 aTrsf2.SetRotation( theAxis, theAngle/2. );
5076 gp_Lin aLine( theAxis );
5077 double aSqTol = theTol * theTol;
5079 SMESHDS_Mesh* aMesh = GetMeshDS();
5081 TNodeOfNodeListMap mapNewNodes;
5082 TElemOfVecOfNnlmiMap mapElemNewNodes;
5083 TTElemOfElemListMap newElemsMap;
5085 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5086 myMesh->NbFaces(ORDER_QUADRATIC) +
5087 myMesh->NbVolumes(ORDER_QUADRATIC) );
5088 // loop on theElemSets
5089 TIDSortedElemSet::iterator itElem;
5090 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5092 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5093 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5094 const SMDS_MeshElement* elem = *itElem;
5095 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5097 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5098 newNodesItVec.reserve( elem->NbNodes() );
5100 // loop on elem nodes
5101 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5102 while ( itN->more() )
5104 const SMDS_MeshNode* node = cast2Node( itN->next() );
5106 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5108 aXYZ.Coord( coord[0], coord[1], coord[2] );
5109 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5111 // check if a node has been already sweeped
5112 TNodeOfNodeListMapItr nIt =
5113 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5114 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5115 if ( listNewNodes.empty() )
5117 // check if we are to create medium nodes between corner ones
5118 bool needMediumNodes = false;
5119 if ( isQuadraticMesh )
5121 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5122 while (it->more() && !needMediumNodes )
5124 const SMDS_MeshElement* invElem = it->next();
5125 if ( invElem != elem && !theElems.count( invElem )) continue;
5126 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5127 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5128 needMediumNodes = true;
5133 const SMDS_MeshNode * newNode = node;
5134 for ( int i = 0; i < theNbSteps; i++ ) {
5136 if ( needMediumNodes ) // create a medium node
5138 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5139 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5140 myLastCreatedNodes.push_back(newNode);
5141 srcNodes.push_back( node );
5142 listNewNodes.push_back( newNode );
5143 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5146 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5148 // create a corner node
5149 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5150 myLastCreatedNodes.push_back(newNode);
5151 srcNodes.push_back( node );
5152 listNewNodes.push_back( newNode );
5155 listNewNodes.push_back( newNode );
5156 // if ( needMediumNodes )
5157 // listNewNodes.push_back( newNode );
5161 newNodesItVec.push_back( nIt );
5163 // make new elements
5164 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5169 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5171 PGroupIDs newGroupIDs;
5172 if ( theMakeGroups )
5173 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5178 //=======================================================================
5179 //function : ExtrusParam
5180 //purpose : standard construction
5181 //=======================================================================
5183 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5184 const int theNbSteps,
5185 const std::list<double>& theScales,
5186 const gp_XYZ* theBasePoint,
5188 const double theTolerance):
5190 myBaseP( Precision::Infinite(), 0, 0 ),
5191 myFlags( theFlags ),
5192 myTolerance( theTolerance ),
5193 myElemsToUse( NULL )
5195 mySteps = new TColStd_HSequenceOfReal;
5196 const double stepSize = theStep.Magnitude();
5197 for (int i=1; i<=theNbSteps; i++ )
5198 mySteps->Append( stepSize );
5200 int nbScales = theScales.size();
5203 if ( IsLinearVariation() && nbScales < theNbSteps )
5205 myScales.reserve( theNbSteps );
5206 std::list<double>::const_iterator scale = theScales.begin();
5207 double prevScale = 1.0;
5208 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5210 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5211 int stDelta = Max( 1, iStep - myScales.size());
5212 double scDelta = ( *scale - prevScale ) / stDelta;
5213 for ( int iStep = 0; iStep < stDelta; ++iStep )
5215 myScales.push_back( prevScale + scDelta );
5216 prevScale = myScales.back();
5223 myScales.assign( theScales.begin(), theScales.end() );
5228 myBaseP = *theBasePoint;
5231 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5232 ( theTolerance > 0 ))
5234 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5238 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose : steps are given explicitly
5245 //=======================================================================
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5248 Handle(TColStd_HSequenceOfReal) theSteps,
5250 const double theTolerance):
5252 mySteps( theSteps ),
5253 myFlags( theFlags ),
5254 myTolerance( theTolerance ),
5255 myElemsToUse( NULL )
5257 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5258 ( theTolerance > 0 ))
5260 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5264 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5268 //=======================================================================
5269 //function : ExtrusParam
5270 //purpose : for extrusion by normal
5271 //=======================================================================
5273 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5274 const int theNbSteps,
5278 mySteps( new TColStd_HSequenceOfReal ),
5279 myFlags( theFlags ),
5281 myElemsToUse( NULL )
5283 for (int i = 0; i < theNbSteps; i++ )
5284 mySteps->Append( theStepSize );
5288 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5292 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5296 //=======================================================================
5297 //function : ExtrusParam::SetElementsToUse
5298 //purpose : stores elements to use for extrusion by normal, depending on
5299 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5300 // define myBaseP for scaling
5301 //=======================================================================
5303 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5304 const TIDSortedElemSet& nodes )
5306 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5308 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5310 myBaseP.SetCoord( 0.,0.,0. );
5311 TIDSortedElemSet newNodes;
5313 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5314 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5316 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5317 TIDSortedElemSet::const_iterator itElem = elements.begin();
5318 for ( ; itElem != elements.end(); itElem++ )
5320 const SMDS_MeshElement* elem = *itElem;
5321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5322 while ( itN->more() ) {
5323 const SMDS_MeshElement* node = itN->next();
5324 if ( newNodes.insert( node ).second )
5325 myBaseP += SMESH_TNodeXYZ( node );
5329 myBaseP /= newNodes.size();
5333 //=======================================================================
5334 //function : ExtrusParam::beginStepIter
5335 //purpose : prepare iteration on steps
5336 //=======================================================================
5338 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5340 myWithMediumNodes = withMediumNodes;
5344 //=======================================================================
5345 //function : ExtrusParam::moreSteps
5346 //purpose : are there more steps?
5347 //=======================================================================
5349 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5351 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5353 //=======================================================================
5354 //function : ExtrusParam::nextStep
5355 //purpose : returns the next step
5356 //=======================================================================
5358 double SMESH_MeshEditor::ExtrusParam::nextStep()
5361 if ( !myCurSteps.empty() )
5363 res = myCurSteps.back();
5364 myCurSteps.pop_back();
5366 else if ( myNextStep <= mySteps->Length() )
5368 myCurSteps.push_back( mySteps->Value( myNextStep ));
5370 if ( myWithMediumNodes )
5372 myCurSteps.back() /= 2.;
5373 myCurSteps.push_back( myCurSteps.back() );
5380 //=======================================================================
5381 //function : ExtrusParam::makeNodesByDir
5382 //purpose : create nodes for standard extrusion
5383 //=======================================================================
5385 int SMESH_MeshEditor::ExtrusParam::
5386 makeNodesByDir( SMESHDS_Mesh* mesh,
5387 const SMDS_MeshNode* srcNode,
5388 std::list<const SMDS_MeshNode*> & newNodes,
5389 const bool makeMediumNodes)
5391 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5394 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5396 p += myDir.XYZ() * nextStep();
5397 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5398 newNodes.push_back( newNode );
5401 if ( !myScales.empty() )
5403 if ( makeMediumNodes && myMediumScales.empty() )
5405 myMediumScales.resize( myScales.size() );
5406 double prevFactor = 1.;
5407 for ( size_t i = 0; i < myScales.size(); ++i )
5409 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5410 prevFactor = myScales[i];
5413 typedef std::vector<double>::iterator ScaleIt;
5414 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5416 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5418 gp_XYZ center = myBaseP;
5419 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5421 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5423 center += myDir.XYZ() * nextStep();
5425 iSc += int( makeMediumNodes );
5426 ScaleIt& scale = scales[ iSc % 2 ];
5428 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5429 xyz = ( *scale * ( xyz - center )) + center;
5430 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5438 //=======================================================================
5439 //function : ExtrusParam::makeNodesByDirAndSew
5440 //purpose : create nodes for standard extrusion with sewing
5441 //=======================================================================
5443 int SMESH_MeshEditor::ExtrusParam::
5444 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5445 const SMDS_MeshNode* srcNode,
5446 std::list<const SMDS_MeshNode*> & newNodes,
5447 const bool makeMediumNodes)
5449 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5452 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5454 P1 += myDir.XYZ() * nextStep();
5456 // try to search in sequence of existing nodes
5457 // if myNodes.size()>0 we 'nave to use given sequence
5458 // else - use all nodes of mesh
5459 const SMDS_MeshNode * node = 0;
5460 if ( myNodes.Length() > 0 ) {
5462 for ( i = 1; i <= myNodes.Length(); i++ ) {
5463 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5464 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5466 node = myNodes.Value(i);
5472 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5473 while(itn->more()) {
5474 SMESH_TNodeXYZ P2( itn->next() );
5475 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5484 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5486 newNodes.push_back( node );
5493 //=======================================================================
5494 //function : ExtrusParam::makeNodesByNormal2D
5495 //purpose : create nodes for extrusion using normals of faces
5496 //=======================================================================
5498 int SMESH_MeshEditor::ExtrusParam::
5499 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5500 const SMDS_MeshNode* srcNode,
5501 std::list<const SMDS_MeshNode*> & newNodes,
5502 const bool makeMediumNodes)
5504 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5506 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5508 // get normals to faces sharing srcNode
5509 vector< gp_XYZ > norms, baryCenters;
5510 gp_XYZ norm, avgNorm( 0,0,0 );
5511 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5512 while ( faceIt->more() )
5514 const SMDS_MeshElement* face = faceIt->next();
5515 if ( myElemsToUse && !myElemsToUse->count( face ))
5517 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5519 norms.push_back( norm );
5521 if ( !alongAvgNorm )
5525 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5526 bc += SMESH_TNodeXYZ( nIt->next() );
5527 baryCenters.push_back( bc / nbN );
5532 if ( norms.empty() ) return 0;
5534 double normSize = avgNorm.Modulus();
5535 if ( normSize < std::numeric_limits<double>::min() )
5538 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5541 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5544 avgNorm /= normSize;
5547 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5550 double stepSize = nextStep();
5552 if ( norms.size() > 1 )
5554 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5556 // translate plane of a face
5557 baryCenters[ iF ] += norms[ iF ] * stepSize;
5559 // find point of intersection of the face plane located at baryCenters[ iF ]
5560 // and avgNorm located at pNew
5561 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5562 double dot = ( norms[ iF ] * avgNorm );
5563 if ( dot < std::numeric_limits<double>::min() )
5564 dot = stepSize * 1e-3;
5565 double step = -( norms[ iF ] * pNew + d ) / dot;
5566 pNew += step * avgNorm;
5571 pNew += stepSize * avgNorm;
5575 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5576 newNodes.push_back( newNode );
5581 //=======================================================================
5582 //function : ExtrusParam::makeNodesByNormal1D
5583 //purpose : create nodes for extrusion using normals of edges
5584 //=======================================================================
5586 int SMESH_MeshEditor::ExtrusParam::
5587 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5588 const SMDS_MeshNode* srcNode,
5589 std::list<const SMDS_MeshNode*> & newNodes,
5590 const bool makeMediumNodes)
5592 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5596 //=======================================================================
5597 //function : ExtrusionSweep
5599 //=======================================================================
5601 SMESH_MeshEditor::PGroupIDs
5602 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5603 const gp_Vec& theStep,
5604 const int theNbSteps,
5605 TTElemOfElemListMap& newElemsMap,
5607 const double theTolerance)
5609 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5610 return ExtrusionSweep( theElems, aParams, newElemsMap );
5614 //=======================================================================
5615 //function : ExtrusionSweep
5617 //=======================================================================
5619 SMESH_MeshEditor::PGroupIDs
5620 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5621 ExtrusParam& theParams,
5622 TTElemOfElemListMap& newElemsMap)
5626 setElemsFirst( theElemSets );
5627 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5628 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5630 // source elements for each generated one
5631 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5632 srcElems.reserve( theElemSets[0].size() );
5633 srcNodes.reserve( theElemSets[1].size() );
5635 const int nbSteps = theParams.NbSteps();
5636 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5638 TNodeOfNodeListMap mapNewNodes;
5639 TElemOfVecOfNnlmiMap mapElemNewNodes;
5641 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5642 myMesh->NbFaces(ORDER_QUADRATIC) +
5643 myMesh->NbVolumes(ORDER_QUADRATIC) );
5645 TIDSortedElemSet::iterator itElem;
5646 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5648 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5649 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5651 // check element type
5652 const SMDS_MeshElement* elem = *itElem;
5653 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5656 const size_t nbNodes = elem->NbNodes();
5657 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5658 newNodesItVec.reserve( nbNodes );
5660 // loop on elem nodes
5661 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5662 while ( itN->more() )
5664 // check if a node has been already sweeped
5665 const SMDS_MeshNode* node = cast2Node( itN->next() );
5666 TNodeOfNodeListMap::iterator nIt =
5667 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5668 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5669 if ( listNewNodes.empty() )
5673 // check if we are to create medium nodes between corner ones
5674 bool needMediumNodes = false;
5675 if ( isQuadraticMesh )
5677 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5678 while (it->more() && !needMediumNodes )
5680 const SMDS_MeshElement* invElem = it->next();
5681 if ( invElem != elem && !theElems.count( invElem )) continue;
5682 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5683 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5684 needMediumNodes = true;
5687 // create nodes for all steps
5688 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5690 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5691 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5693 myLastCreatedNodes.push_back( *newNodesIt );
5694 srcNodes.push_back( node );
5699 break; // newNodesItVec will be shorter than nbNodes
5702 newNodesItVec.push_back( nIt );
5704 // make new elements
5705 if ( newNodesItVec.size() == nbNodes )
5706 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5710 if ( theParams.ToMakeBoundary() ) {
5711 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5713 PGroupIDs newGroupIDs;
5714 if ( theParams.ToMakeGroups() )
5715 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5720 //=======================================================================
5721 //function : ExtrusionAlongTrack
5723 //=======================================================================
5724 SMESH_MeshEditor::Extrusion_Error
5725 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5726 SMESH_subMesh* theTrack,
5727 const SMDS_MeshNode* theN1,
5728 const bool theHasAngles,
5729 list<double>& theAngles,
5730 const bool theLinearVariation,
5731 const bool theHasRefPoint,
5732 const gp_Pnt& theRefPoint,
5733 const bool theMakeGroups)
5738 std::list<double> aPrms;
5739 TIDSortedElemSet::iterator itElem;
5742 TopoDS_Edge aTrackEdge;
5743 TopoDS_Vertex aV1, aV2;
5745 SMDS_ElemIteratorPtr aItE;
5746 SMDS_NodeIteratorPtr aItN;
5747 SMDSAbs_ElementType aTypeE;
5749 TNodeOfNodeListMap mapNewNodes;
5752 aNbE = theElements[0].size() + theElements[1].size();
5755 return EXTR_NO_ELEMENTS;
5757 // 1.1 Track Pattern
5760 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5762 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5763 theHasAngles, theAngles, theLinearVariation,
5764 theHasRefPoint, theRefPoint, theMakeGroups );
5766 aItE = pSubMeshDS->GetElements();
5767 while ( aItE->more() ) {
5768 const SMDS_MeshElement* pE = aItE->next();
5769 aTypeE = pE->GetType();
5770 // Pattern must contain links only
5771 if ( aTypeE != SMDSAbs_Edge )
5772 return EXTR_PATH_NOT_EDGE;
5775 list<SMESH_MeshEditor_PathPoint> fullList;
5777 const TopoDS_Shape& aS = theTrack->GetSubShape();
5778 // Sub-shape for the Pattern must be an Edge or Wire
5779 if( aS.ShapeType() == TopAbs_EDGE ) {
5780 aTrackEdge = TopoDS::Edge( aS );
5781 // the Edge must not be degenerated
5782 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5783 return EXTR_BAD_PATH_SHAPE;
5784 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5785 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5786 const SMDS_MeshNode* aN1 = aItN->next();
5787 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5788 const SMDS_MeshNode* aN2 = aItN->next();
5789 // starting node must be aN1 or aN2
5790 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5791 return EXTR_BAD_STARTING_NODE;
5792 aItN = pSubMeshDS->GetNodes();
5793 while ( aItN->more() ) {
5794 const SMDS_MeshNode* pNode = aItN->next();
5795 const SMDS_EdgePosition* pEPos =
5796 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5797 double aT = pEPos->GetUParameter();
5798 aPrms.push_back( aT );
5800 //Extrusion_Error err =
5801 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5802 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5803 list< SMESH_subMesh* > LSM;
5804 TopTools_SequenceOfShape Edges;
5805 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5806 while(itSM->more()) {
5807 SMESH_subMesh* SM = itSM->next();
5809 const TopoDS_Shape& aS = SM->GetSubShape();
5812 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5813 int startNid = theN1->GetID();
5814 TColStd_MapOfInteger UsedNums;
5816 int NbEdges = Edges.Length();
5818 for(; i<=NbEdges; i++) {
5820 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5821 for(; itLSM!=LSM.end(); itLSM++) {
5823 if(UsedNums.Contains(k)) continue;
5824 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5825 SMESH_subMesh* locTrack = *itLSM;
5826 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5827 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5828 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5829 const SMDS_MeshNode* aN1 = aItN->next();
5830 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5831 const SMDS_MeshNode* aN2 = aItN->next();
5832 // starting node must be aN1 or aN2
5833 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5834 // 2. Collect parameters on the track edge
5836 aItN = locMeshDS->GetNodes();
5837 while ( aItN->more() ) {
5838 const SMDS_MeshNode* pNode = aItN->next();
5839 const SMDS_EdgePosition* pEPos =
5840 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5841 double aT = pEPos->GetUParameter();
5842 aPrms.push_back( aT );
5844 list<SMESH_MeshEditor_PathPoint> LPP;
5845 //Extrusion_Error err =
5846 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5847 LLPPs.push_back(LPP);
5849 // update startN for search following egde
5850 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5851 else startNid = aN1->GetID();
5855 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5856 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5857 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5858 for(; itPP!=firstList.end(); itPP++) {
5859 fullList.push_back( *itPP );
5861 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5862 fullList.pop_back();
5864 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5865 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5866 itPP = currList.begin();
5867 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5868 gp_Dir D1 = PP1.Tangent();
5869 gp_Dir D2 = PP2.Tangent();
5870 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5871 (D1.Z()+D2.Z())/2 ) );
5872 PP1.SetTangent(Dnew);
5873 fullList.push_back(PP1);
5875 for(; itPP!=firstList.end(); itPP++) {
5876 fullList.push_back( *itPP );
5878 PP1 = fullList.back();
5879 fullList.pop_back();
5881 // if wire not closed
5882 fullList.push_back(PP1);
5886 return EXTR_BAD_PATH_SHAPE;
5889 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5890 theHasRefPoint, theRefPoint, theMakeGroups);
5894 //=======================================================================
5895 //function : ExtrusionAlongTrack
5897 //=======================================================================
5898 SMESH_MeshEditor::Extrusion_Error
5899 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5900 SMESH_Mesh* theTrack,
5901 const SMDS_MeshNode* theN1,
5902 const bool theHasAngles,
5903 list<double>& theAngles,
5904 const bool theLinearVariation,
5905 const bool theHasRefPoint,
5906 const gp_Pnt& theRefPoint,
5907 const bool theMakeGroups)
5912 std::list<double> aPrms;
5913 TIDSortedElemSet::iterator itElem;
5916 TopoDS_Edge aTrackEdge;
5917 TopoDS_Vertex aV1, aV2;
5919 SMDS_ElemIteratorPtr aItE;
5920 SMDS_NodeIteratorPtr aItN;
5921 SMDSAbs_ElementType aTypeE;
5923 TNodeOfNodeListMap mapNewNodes;
5926 aNbE = theElements[0].size() + theElements[1].size();
5929 return EXTR_NO_ELEMENTS;
5931 // 1.1 Track Pattern
5934 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5936 aItE = pMeshDS->elementsIterator();
5937 while ( aItE->more() ) {
5938 const SMDS_MeshElement* pE = aItE->next();
5939 aTypeE = pE->GetType();
5940 // Pattern must contain links only
5941 if ( aTypeE != SMDSAbs_Edge )
5942 return EXTR_PATH_NOT_EDGE;
5945 list<SMESH_MeshEditor_PathPoint> fullList;
5947 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5949 if ( !theTrack->HasShapeToMesh() ) {
5950 //Mesh without shape
5951 const SMDS_MeshNode* currentNode = NULL;
5952 const SMDS_MeshNode* prevNode = theN1;
5953 std::vector<const SMDS_MeshNode*> aNodesList;
5954 aNodesList.push_back(theN1);
5955 int nbEdges = 0, conn=0;
5956 const SMDS_MeshElement* prevElem = NULL;
5957 const SMDS_MeshElement* currentElem = NULL;
5958 int totalNbEdges = theTrack->NbEdges();
5959 SMDS_ElemIteratorPtr nIt;
5962 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5963 return EXTR_BAD_STARTING_NODE;
5966 conn = nbEdgeConnectivity(theN1);
5968 return EXTR_PATH_NOT_EDGE;
5970 aItE = theN1->GetInverseElementIterator();
5971 prevElem = aItE->next();
5972 currentElem = prevElem;
5974 if(totalNbEdges == 1 ) {
5975 nIt = currentElem->nodesIterator();
5976 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5977 if(currentNode == prevNode)
5978 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979 aNodesList.push_back(currentNode);
5981 nIt = currentElem->nodesIterator();
5982 while( nIt->more() ) {
5983 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5984 if(currentNode == prevNode)
5985 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986 aNodesList.push_back(currentNode);
5988 //case of the closed mesh
5989 if(currentNode == theN1) {
5994 conn = nbEdgeConnectivity(currentNode);
5996 return EXTR_PATH_NOT_EDGE;
5997 }else if( conn == 1 && nbEdges > 0 ) {
6002 prevNode = currentNode;
6003 aItE = currentNode->GetInverseElementIterator();
6004 currentElem = aItE->next();
6005 if( currentElem == prevElem)
6006 currentElem = aItE->next();
6007 nIt = currentElem->nodesIterator();
6008 prevElem = currentElem;
6014 if(nbEdges != totalNbEdges)
6015 return EXTR_PATH_NOT_EDGE;
6017 TopTools_SequenceOfShape Edges;
6018 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6019 int startNid = theN1->GetID();
6020 for ( size_t i = 1; i < aNodesList.size(); i++ )
6022 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6023 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6024 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6025 list<SMESH_MeshEditor_PathPoint> LPP;
6027 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6028 LLPPs.push_back(LPP);
6029 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6030 else startNid = aNodesList[i-1]->GetID();
6033 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6034 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6035 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6036 for(; itPP!=firstList.end(); itPP++) {
6037 fullList.push_back( *itPP );
6040 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6041 SMESH_MeshEditor_PathPoint PP2;
6042 fullList.pop_back();
6044 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6045 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6046 itPP = currList.begin();
6047 PP2 = currList.front();
6048 gp_Dir D1 = PP1.Tangent();
6049 gp_Dir D2 = PP2.Tangent();
6050 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6051 PP1.SetTangent(Dnew);
6052 fullList.push_back(PP1);
6054 for(; itPP!=currList.end(); itPP++) {
6055 fullList.push_back( *itPP );
6057 PP1 = fullList.back();
6058 fullList.pop_back();
6060 fullList.push_back(PP1);
6062 } // Sub-shape for the Pattern must be an Edge or Wire
6063 else if ( aS.ShapeType() == TopAbs_EDGE )
6065 aTrackEdge = TopoDS::Edge( aS );
6066 // the Edge must not be degenerated
6067 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6068 return EXTR_BAD_PATH_SHAPE;
6069 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6070 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6071 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6072 // starting node must be aN1 or aN2
6073 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6074 return EXTR_BAD_STARTING_NODE;
6075 aItN = pMeshDS->nodesIterator();
6076 while ( aItN->more() ) {
6077 const SMDS_MeshNode* pNode = aItN->next();
6078 if( pNode==aN1 || pNode==aN2 ) continue;
6079 const SMDS_EdgePosition* pEPos =
6080 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6081 double aT = pEPos->GetUParameter();
6082 aPrms.push_back( aT );
6084 //Extrusion_Error err =
6085 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6087 else if( aS.ShapeType() == TopAbs_WIRE ) {
6088 list< SMESH_subMesh* > LSM;
6089 TopTools_SequenceOfShape Edges;
6090 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6091 for(; eExp.More(); eExp.Next()) {
6092 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6093 if( SMESH_Algo::isDegenerated(E) ) continue;
6094 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6100 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6101 TopoDS_Vertex aVprev;
6102 TColStd_MapOfInteger UsedNums;
6103 int NbEdges = Edges.Length();
6105 for(; i<=NbEdges; i++) {
6107 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6108 for(; itLSM!=LSM.end(); itLSM++) {
6110 if(UsedNums.Contains(k)) continue;
6111 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6112 SMESH_subMesh* locTrack = *itLSM;
6113 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6114 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6115 bool aN1isOK = false, aN2isOK = false;
6116 if ( aVprev.IsNull() ) {
6117 // if previous vertex is not yet defined, it means that we in the beginning of wire
6118 // and we have to find initial vertex corresponding to starting node theN1
6119 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6120 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6121 // starting node must be aN1 or aN2
6122 aN1isOK = ( aN1 && aN1 == theN1 );
6123 aN2isOK = ( aN2 && aN2 == theN1 );
6126 // we have specified ending vertex of the previous edge on the previous iteration
6127 // and we have just to check that it corresponds to any vertex in current segment
6128 aN1isOK = aVprev.IsSame( aV1 );
6129 aN2isOK = aVprev.IsSame( aV2 );
6131 if ( !aN1isOK && !aN2isOK ) continue;
6132 // 2. Collect parameters on the track edge
6134 aItN = locMeshDS->GetNodes();
6135 while ( aItN->more() ) {
6136 const SMDS_MeshNode* pNode = aItN->next();
6137 const SMDS_EdgePosition* pEPos =
6138 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6139 double aT = pEPos->GetUParameter();
6140 aPrms.push_back( aT );
6142 list<SMESH_MeshEditor_PathPoint> LPP;
6143 //Extrusion_Error err =
6144 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145 LLPPs.push_back(LPP);
6147 // update startN for search following egde
6148 if ( aN1isOK ) aVprev = aV2;
6153 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155 fullList.splice( fullList.end(), firstList );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163 gp_Dir D1 = PP1.Tangent();
6164 gp_Dir D2 = PP2.Tangent();
6165 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166 PP1.SetTangent(Dnew);
6167 fullList.push_back(PP1);
6168 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169 PP1 = fullList.back();
6170 fullList.pop_back();
6172 // if wire not closed
6173 fullList.push_back(PP1);
6177 return EXTR_BAD_PATH_SHAPE;
6180 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181 theHasRefPoint, theRefPoint, theMakeGroups);
6185 //=======================================================================
6186 //function : MakeEdgePathPoints
6187 //purpose : auxilary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6191 const TopoDS_Edge& aTrackEdge,
6193 list<SMESH_MeshEditor_PathPoint>& LPP)
6195 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6197 aTolVec2=aTolVec*aTolVec;
6199 TopoDS_Vertex aV1, aV2;
6200 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203 // 2. Collect parameters on the track edge
6204 aPrms.push_front( aT1 );
6205 aPrms.push_back( aT2 );
6208 if( FirstIsStart ) {
6219 SMESH_MeshEditor_PathPoint aPP;
6220 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221 std::list<double>::iterator aItD = aPrms.begin();
6222 for(; aItD != aPrms.end(); ++aItD) {
6226 aC3D->D1( aT, aP3D, aVec );
6227 aL2 = aVec.SquareMagnitude();
6228 if ( aL2 < aTolVec2 )
6229 return EXTR_CANT_GET_TANGENT;
6230 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6232 aPP.SetTangent( aTgt );
6233 aPP.SetParameter( aT );
6240 //=======================================================================
6241 //function : MakeExtrElements
6242 //purpose : auxilary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6246 list<SMESH_MeshEditor_PathPoint>& fullList,
6247 const bool theHasAngles,
6248 list<double>& theAngles,
6249 const bool theLinearVariation,
6250 const bool theHasRefPoint,
6251 const gp_Pnt& theRefPoint,
6252 const bool theMakeGroups)
6254 const int aNbTP = fullList.size();
6257 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258 LinearAngleVariation(aNbTP-1, theAngles);
6260 // fill vector of path points with angles
6261 vector<SMESH_MeshEditor_PathPoint> aPPs;
6262 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263 list<double>::iterator itAngles = theAngles.begin();
6264 aPPs.push_back( *itPP++ );
6265 for( ; itPP != fullList.end(); itPP++) {
6266 aPPs.push_back( *itPP );
6267 if ( theHasAngles && itAngles != theAngles.end() )
6268 aPPs.back().SetAngle( *itAngles++ );
6271 TNodeOfNodeListMap mapNewNodes;
6272 TElemOfVecOfNnlmiMap mapElemNewNodes;
6273 TTElemOfElemListMap newElemsMap;
6274 TIDSortedElemSet::iterator itElem;
6275 // source elements for each generated one
6276 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6278 // 3. Center of rotation aV0
6279 gp_Pnt aV0 = theRefPoint;
6280 if ( !theHasRefPoint )
6282 gp_XYZ aGC( 0.,0.,0. );
6283 TIDSortedElemSet newNodes;
6285 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6287 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288 itElem = theElements.begin();
6289 for ( ; itElem != theElements.end(); itElem++ )
6291 const SMDS_MeshElement* elem = *itElem;
6292 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6293 while ( itN->more() ) {
6294 const SMDS_MeshElement* node = itN->next();
6295 if ( newNodes.insert( node ).second )
6296 aGC += SMESH_TNodeXYZ( node );
6300 aGC /= newNodes.size();
6302 } // if (!theHasRefPoint) {
6304 // 4. Processing the elements
6305 SMESHDS_Mesh* aMesh = GetMeshDS();
6306 list<const SMDS_MeshNode*> emptyList;
6308 setElemsFirst( theElemSets );
6309 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6311 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6314 const SMDS_MeshElement* elem = *itElem;
6316 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317 newNodesItVec.reserve( elem->NbNodes() );
6319 // loop on elem nodes
6321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322 while ( itN->more() )
6325 // check if a node has been already processed
6326 const SMDS_MeshNode* node = cast2Node( itN->next() );
6327 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329 if ( listNewNodes.empty() )
6332 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334 gp_Ax1 anAx1, anAxT1T0;
6335 gp_Dir aDT1x, aDT0x, aDT1T0;
6340 aPN0 = SMESH_TNodeXYZ( node );
6342 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6344 aDT0x= aPP0.Tangent();
6346 for ( int j = 1; j < aNbTP; ++j ) {
6347 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6349 aDT1x = aPP1.Tangent();
6350 aAngle1x = aPP1.Angle();
6352 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6354 gp_Vec aV01x( aP0x, aP1x );
6355 aTrsf.SetTranslation( aV01x );
6358 aV1x = aV0x.Transformed( aTrsf );
6359 aPN1 = aPN0.Transformed( aTrsf );
6361 // rotation 1 [ T1,T0 ]
6362 aAngleT1T0=-aDT1x.Angle( aDT0x );
6363 if (fabs(aAngleT1T0) > aTolAng)
6366 anAxT1T0.SetLocation( aV1x );
6367 anAxT1T0.SetDirection( aDT1T0 );
6368 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6370 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374 if ( theHasAngles ) {
6375 anAx1.SetLocation( aV1x );
6376 anAx1.SetDirection( aDT1x );
6377 aTrsfRot.SetRotation( anAx1, aAngle1x );
6379 aPN1 = aPN1.Transformed( aTrsfRot );
6383 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6385 // create additional node
6386 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388 myLastCreatedNodes.push_back(newNode);
6389 srcNodes.push_back( node );
6390 listNewNodes.push_back( newNode );
6392 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393 myLastCreatedNodes.push_back(newNode);
6394 srcNodes.push_back( node );
6395 listNewNodes.push_back( newNode );
6403 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6405 // if current elem is quadratic and current node is not medium
6406 // we have to check - may be it is needed to insert additional nodes
6407 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408 if ((int) listNewNodes.size() == aNbTP-1 )
6410 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411 gp_XYZ P(node->X(), node->Y(), node->Z());
6412 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6414 for(i=0; i<aNbTP-1; i++) {
6415 const SMDS_MeshNode* N = *it;
6416 double x = ( N->X() + P.X() )/2.;
6417 double y = ( N->Y() + P.Y() )/2.;
6418 double z = ( N->Z() + P.Z() )/2.;
6419 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420 srcNodes.push_back( node );
6421 myLastCreatedNodes.push_back(newN);
6424 P = gp_XYZ(N->X(),N->Y(),N->Z());
6426 listNewNodes.clear();
6427 for(i=0; i<2*(aNbTP-1); i++) {
6428 listNewNodes.push_back(aNodes[i]);
6433 newNodesItVec.push_back( nIt );
6436 // make new elements
6437 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6441 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6443 if ( theMakeGroups )
6444 generateGroups( srcNodes, srcElems, "extruded");
6450 //=======================================================================
6451 //function : LinearAngleVariation
6452 //purpose : spread values over nbSteps
6453 //=======================================================================
6455 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6456 list<double>& Angles)
6458 int nbAngles = Angles.size();
6459 if( nbSteps > nbAngles && nbAngles > 0 )
6461 vector<double> theAngles(nbAngles);
6462 theAngles.assign( Angles.begin(), Angles.end() );
6465 double rAn2St = double( nbAngles ) / double( nbSteps );
6466 double angPrev = 0, angle;
6467 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6469 double angCur = rAn2St * ( iSt+1 );
6470 double angCurFloor = floor( angCur );
6471 double angPrevFloor = floor( angPrev );
6472 if ( angPrevFloor == angCurFloor )
6473 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6475 int iP = int( angPrevFloor );
6476 double angPrevCeil = ceil(angPrev);
6477 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6479 int iC = int( angCurFloor );
6480 if ( iC < nbAngles )
6481 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6483 iP = int( angPrevCeil );
6485 angle += theAngles[ iC ];
6487 res.push_back(angle);
6495 //================================================================================
6497 * \brief Move or copy theElements applying theTrsf to their nodes
6498 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499 * \param theTrsf - transformation to apply
6500 * \param theCopy - if true, create translated copies of theElems
6501 * \param theMakeGroups - if true and theCopy, create translated groups
6502 * \param theTargetMesh - mesh to copy translated elements into
6503 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6505 //================================================================================
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509 const gp_Trsf& theTrsf,
6511 const bool theMakeGroups,
6512 SMESH_Mesh* theTargetMesh)
6515 myLastCreatedElems.reserve( theElems.size() );
6517 bool needReverse = false;
6518 string groupPostfix;
6519 switch ( theTrsf.Form() ) {
6522 groupPostfix = "mirrored";
6525 groupPostfix = "mirrored";
6529 groupPostfix = "mirrored";
6532 groupPostfix = "rotated";
6534 case gp_Translation:
6535 groupPostfix = "translated";
6538 groupPostfix = "scaled";
6540 case gp_CompoundTrsf: // different scale by axis
6541 groupPostfix = "scaled";
6544 needReverse = false;
6545 groupPostfix = "transformed";
6548 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549 SMESHDS_Mesh* aMesh = GetMeshDS();
6551 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553 SMESH_MeshEditor::ElemFeatures elemType;
6555 // map old node to new one
6556 TNodeNodeMap nodeMap;
6558 // elements sharing moved nodes; those of them which have all
6559 // nodes mirrored but are not in theElems are to be reversed
6560 TIDSortedElemSet inverseElemSet;
6562 // source elements for each generated one
6563 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6565 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566 TIDSortedElemSet orphanNode;
6568 if ( theElems.empty() ) // transform the whole mesh
6571 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572 while ( eIt->more() ) theElems.insert( eIt->next() );
6574 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575 while ( nIt->more() )
6577 const SMDS_MeshNode* node = nIt->next();
6578 if ( node->NbInverseElements() == 0)
6579 orphanNode.insert( node );
6583 // loop on elements to transform nodes : first orphan nodes then elems
6584 TIDSortedElemSet::iterator itElem;
6585 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586 for (int i=0; i<2; i++)
6587 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6589 const SMDS_MeshElement* elem = *itElem;
6593 // loop on elem nodes
6595 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596 while ( itN->more() )
6598 const SMDS_MeshNode* node = cast2Node( itN->next() );
6599 // check if a node has been already transformed
6600 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601 nodeMap.insert( make_pair ( node, node ));
6602 if ( !n2n_isnew.second )
6605 node->GetXYZ( coord );
6606 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607 if ( theTargetMesh ) {
6608 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609 n2n_isnew.first->second = newNode;
6610 myLastCreatedNodes.push_back(newNode);
6611 srcNodes.push_back( node );
6613 else if ( theCopy ) {
6614 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615 n2n_isnew.first->second = newNode;
6616 myLastCreatedNodes.push_back(newNode);
6617 srcNodes.push_back( node );
6620 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621 // node position on shape becomes invalid
6622 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623 ( SMDS_SpacePosition::originSpacePosition() );
6626 // keep inverse elements
6627 if ( !theCopy && !theTargetMesh && needReverse ) {
6628 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629 while ( invElemIt->more() ) {
6630 const SMDS_MeshElement* iel = invElemIt->next();
6631 inverseElemSet.insert( iel );
6635 } // loop on elems in { &orphanNode, &theElems };
6637 // either create new elements or reverse mirrored ones
6638 if ( !theCopy && !needReverse && !theTargetMesh )
6641 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6643 // Replicate or reverse elements
6645 std::vector<int> iForw;
6646 vector<const SMDS_MeshNode*> nodes;
6647 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6649 const SMDS_MeshElement* elem = *itElem;
6650 if ( !elem ) continue;
6652 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653 size_t nbNodes = elem->NbNodes();
6654 if ( geomType == SMDSGeom_NONE ) continue; // node
6656 nodes.resize( nbNodes );
6658 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6660 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6664 bool allTransformed = true;
6665 int nbFaces = aPolyedre->NbFaces();
6666 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6668 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6671 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673 if ( nodeMapIt == nodeMap.end() )
6674 allTransformed = false; // not all nodes transformed
6676 nodes.push_back((*nodeMapIt).second);
6678 if ( needReverse && allTransformed )
6679 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6681 if ( !allTransformed )
6682 continue; // not all nodes transformed
6684 else // ----------------------- the rest element types
6686 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688 const vector<int>& i = needReverse ? iRev : iForw;
6690 // find transformed nodes
6692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693 while ( itN->more() ) {
6694 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696 if ( nodeMapIt == nodeMap.end() )
6697 break; // not all nodes transformed
6698 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6700 if ( iNode != nbNodes )
6701 continue; // not all nodes transformed
6705 // copy in this or a new mesh
6706 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707 srcElems.push_back( elem );
6710 // reverse element as it was reversed by transformation
6712 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6715 } // loop on elements
6717 if ( editor && editor != this )
6718 myLastCreatedElems.swap( editor->myLastCreatedElems );
6720 PGroupIDs newGroupIDs;
6722 if ( ( theMakeGroups && theCopy ) ||
6723 ( theMakeGroups && theTargetMesh ) )
6724 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6729 //================================================================================
6731 * \brief Make an offset mesh from a source 2D mesh
6732 * \param [in] theElements - source faces
6733 * \param [in] theValue - offset value
6734 * \param [out] theTgtMesh - a mesh to add offset elements to
6735 * \param [in] theMakeGroups - to generate groups
6736 * \return PGroupIDs - IDs of created groups
6738 //================================================================================
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741 const double theValue,
6742 SMESH_Mesh* theTgtMesh,
6743 const bool theMakeGroups,
6744 const bool theFixSelfIntersection)
6746 SMESHDS_Mesh* meshDS = GetMeshDS();
6747 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748 SMESH_MeshEditor tgtEditor( theTgtMesh );
6750 SMDS_ElemIteratorPtr eIt;
6751 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752 else eIt = SMESHUtils::elemSetIterator( theElements );
6754 SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755 SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756 std::unique_ptr< SMDS_Mesh > offsetMesh
6757 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758 theFixSelfIntersection,
6759 new2OldFaces, new2OldNodes ));
6761 offsetMesh->Modified();
6762 offsetMesh->CompactMesh(); // make IDs start from 1
6764 // source elements for each generated one
6765 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766 srcElems.reserve( new2OldFaces.size() );
6767 srcNodes.reserve( new2OldNodes.size() );
6770 myLastCreatedElems.reserve( new2OldFaces.size() );
6771 myLastCreatedNodes.reserve( new2OldNodes.size() );
6773 // copy offsetMesh to theTgtMesh
6775 int idShift = meshDS->MaxNodeID();
6776 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6779 if ( n->NbInverseElements() > 0 )
6781 const SMDS_MeshNode* n2 =
6782 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783 myLastCreatedNodes.push_back( n2 );
6784 srcNodes.push_back( new2OldNodes[ i ].second );
6788 ElemFeatures elemType;
6789 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6793 elemType.myNodes.clear();
6794 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6796 const SMDS_MeshNode* n2 = nIt->next();
6797 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6799 tgtEditor.AddElement( elemType.myNodes, elemType );
6800 srcElems.push_back( new2OldFaces[ i ].second );
6803 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6805 PGroupIDs newGroupIDs;
6806 if ( theMakeGroups )
6807 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6812 //=======================================================================
6814 * \brief Create groups of elements made during transformation
6815 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6816 * \param elemGens - elements making corresponding myLastCreatedElems
6817 * \param postfix - to push_back to names of new groups
6818 * \param targetMesh - mesh to create groups in
6819 * \param topPresent - is there are "top" elements that are created by sweeping
6821 //=======================================================================
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825 const SMESH_SequenceOfElemPtr& elemGens,
6826 const std::string& postfix,
6827 SMESH_Mesh* targetMesh,
6828 const bool topPresent)
6830 PGroupIDs newGroupIDs( new list<int> );
6831 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6833 // Sort existing groups by types and collect their names
6835 // containers to store an old group and generated new ones;
6836 // 1st new group is for result elems of different type than a source one;
6837 // 2nd new group is for same type result elems ("top" group at extrusion)
6839 using boost::make_tuple;
6840 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6844 set< string > groupNames;
6846 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847 if ( !groupIt->more() ) return newGroupIDs;
6849 int newGroupID = mesh->GetGroupIds().back()+1;
6850 while ( groupIt->more() )
6852 SMESH_Group * group = groupIt->next();
6853 if ( !group ) continue;
6854 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855 if ( !groupDS || groupDS->IsEmpty() ) continue;
6856 groupNames.insert ( group->GetName() );
6857 groupDS->SetStoreName( group->GetName() );
6858 const SMDSAbs_ElementType type = groupDS->GetType();
6859 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6865 // Loop on nodes and elements to add them in new groups
6867 vector< const SMDS_MeshElement* > resultElems;
6868 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6870 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6871 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872 if ( gens.size() != elems.size() )
6873 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6875 // loop on created elements
6876 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6878 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879 if ( !sourceElem ) {
6880 MESSAGE("generateGroups(): NULL source element");
6883 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884 if ( groupsOldNew.empty() ) { // no groups of this type at all
6885 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886 ++iElem; // skip all elements made by sourceElem
6889 // collect all elements made by the iElem-th sourceElem
6890 resultElems.clear();
6891 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892 if ( resElem != sourceElem )
6893 resultElems.push_back( resElem );
6894 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896 if ( resElem != sourceElem )
6897 resultElems.push_back( resElem );
6899 const SMDS_MeshElement* topElem = 0;
6900 if ( isNodes ) // there must be a top element
6902 topElem = resultElems.back();
6903 resultElems.pop_back();
6907 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6911 topElem = *resElemIt;
6912 *resElemIt = 0; // erase *resElemIt
6916 // add resultElems to groups originted from ones the sourceElem belongs to
6917 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6920 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6923 // fill in a new group
6924 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6928 newGroup.Add( *resElemIt );
6930 // fill a "top" group
6933 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934 newTopGroup.Add( topElem );
6938 } // loop on created elements
6939 }// loop on nodes and elements
6941 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6943 list<int> topGrouIds;
6944 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6946 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6947 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948 orderedOldNewGroups[i]->get<2>() };
6949 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6951 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952 if ( newGroupDS->IsEmpty() )
6954 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6959 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6962 const bool isTop = ( topPresent &&
6963 newGroupDS->GetType() == oldGroupDS->GetType() &&
6966 string name = oldGroupDS->GetStoreName();
6967 { // remove trailing whitespaces (issue 22599)
6968 size_t size = name.size();
6969 while ( size > 1 && isspace( name[ size-1 ]))
6971 if ( size != name.size() )
6973 name.resize( size );
6974 oldGroupDS->SetStoreName( name.c_str() );
6977 if ( !targetMesh ) {
6978 string suffix = ( isTop ? "top": postfix.c_str() );
6982 while ( !groupNames.insert( name ).second ) // name exists
6983 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6988 newGroupDS->SetStoreName( name.c_str() );
6990 // make a SMESH_Groups
6991 mesh->AddGroup( newGroupDS );
6993 topGrouIds.push_back( newGroupDS->GetID() );
6995 newGroupIDs->push_back( newGroupDS->GetID() );
6999 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7004 //================================================================================
7006 * * \brief Return list of group of nodes close to each other within theTolerance
7007 * * Search among theNodes or in the whole mesh if theNodes is empty using
7008 * * an Octree algorithm
7009 * \param [in,out] theNodes - the nodes to treat
7010 * \param [in] theTolerance - the tolerance
7011 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7012 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7013 * corner and medium nodes in separate groups
7015 //================================================================================
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7018 const double theTolerance,
7019 TListOfListOfNodes & theGroupsOfNodes,
7020 bool theSeparateCornersAndMedium)
7024 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7025 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7026 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027 theSeparateCornersAndMedium = false;
7029 TIDSortedNodeSet& corners = theNodes;
7030 TIDSortedNodeSet medium;
7032 if ( theNodes.empty() ) // get all nodes in the mesh
7034 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7036 if ( theSeparateCornersAndMedium )
7037 while ( nIt->more() )
7039 const SMDS_MeshNode* n = nIt->next();
7040 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041 nodeSet->insert( nodeSet->end(), n );
7044 while ( nIt->more() )
7045 theNodes.insert( theNodes.end(),nIt->next() );
7047 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7049 TIDSortedNodeSet::iterator nIt = corners.begin();
7050 while ( nIt != corners.end() )
7051 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7053 medium.insert( medium.end(), *nIt );
7054 corners.erase( nIt++ );
7062 if ( !corners.empty() )
7063 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064 if ( !medium.empty() )
7065 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose : split a chain of nodes into several closed chains
7071 //=======================================================================
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074 vector<const SMDS_MeshNode *>& poly_nodes,
7075 vector<int>& quantities) const
7077 int nbNodes = faceNodes.size();
7078 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7082 size_t prevNbQuant = quantities.size();
7084 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086 map< const SMDS_MeshNode*, int >::iterator nInd;
7088 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089 simpleNodes.push_back( faceNodes[0] );
7090 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7092 if ( faceNodes[ iCur ] != simpleNodes.back() )
7094 int index = simpleNodes.size();
7095 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096 int prevIndex = nInd->second;
7097 if ( prevIndex < index )
7100 int loopLen = index - prevIndex;
7103 // store the sub-loop
7104 quantities.push_back( loopLen );
7105 for ( int i = prevIndex; i < index; i++ )
7106 poly_nodes.push_back( simpleNodes[ i ]);
7108 simpleNodes.resize( prevIndex+1 );
7112 simpleNodes.push_back( faceNodes[ iCur ]);
7117 if ( simpleNodes.size() > 2 )
7119 quantities.push_back( simpleNodes.size() );
7120 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7123 return quantities.size() - prevNbQuant;
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose : In each group, the cdr of nodes are substituted by the first one
7130 //=======================================================================
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7136 SMESHDS_Mesh* aMesh = GetMeshDS();
7138 TNodeNodeMap nodeNodeMap; // node to replace - new node
7139 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7140 list< int > rmElemIds, rmNodeIds;
7142 // Fill nodeNodeMap and elems
7144 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7145 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7147 list<const SMDS_MeshNode*>& nodes = *grIt;
7148 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7149 const SMDS_MeshNode* nToKeep = *nIt;
7150 for ( ++nIt; nIt != nodes.end(); nIt++ )
7152 const SMDS_MeshNode* nToRemove = *nIt;
7153 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7154 if ( nToRemove != nToKeep )
7156 rmNodeIds.push_back( nToRemove->GetID() );
7157 AddToSameGroups( nToKeep, nToRemove, aMesh );
7158 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7159 // after MergeNodes() w/o creating node in place of merged ones.
7160 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7161 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7162 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7163 sm->SetIsAlwaysComputed( true );
7165 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7166 while ( invElemIt->more() ) {
7167 const SMDS_MeshElement* elem = invElemIt->next();
7172 // Change element nodes or remove an element
7174 set<const SMDS_MeshNode*> nodeSet;
7175 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7177 ElemFeatures elemType;
7179 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7180 for ( ; eIt != elems.end(); eIt++ )
7182 const SMDS_MeshElement* elem = *eIt;
7183 const int nbNodes = elem->NbNodes();
7184 const int aShapeId = FindShape( elem );
7185 SMDSAbs_EntityType entity = elem->GetEntityType();
7188 curNodes.resize( nbNodes );
7189 uniqueNodes.resize( nbNodes );
7190 iRepl.resize( nbNodes );
7191 int iUnique = 0, iCur = 0, nbRepl = 0;
7193 // get new seq of nodes
7194 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7195 while ( itN->more() )
7197 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7199 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7200 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7202 { ////////// BUG 0020185: begin
7203 bool stopRecur = false;
7204 set<const SMDS_MeshNode*> nodesRecur;
7205 nodesRecur.insert(n);
7206 while (!stopRecur) {
7207 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7208 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7209 n = (*nnIt_i).second;
7210 if (!nodesRecur.insert(n).second) {
7211 // error: recursive dependency
7218 } ////////// BUG 0020185: end
7220 curNodes[ iCur ] = n;
7221 bool isUnique = nodeSet.insert( n ).second;
7223 uniqueNodes[ iUnique++ ] = n;
7225 iRepl[ nbRepl++ ] = iCur;
7229 // Analyse element topology after replacement
7232 int nbUniqueNodes = nodeSet.size();
7233 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7235 if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7237 if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7239 elemType.Init( elem );
7240 const bool isQuad = elemType.myIsQuad;
7242 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7243 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7245 // a polygon can divide into several elements
7246 vector<const SMDS_MeshNode *> polygons_nodes;
7247 vector<int> quantities;
7248 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7251 vector<const SMDS_MeshNode *> face_nodes;
7253 for (int iface = 0; iface < nbNew; iface++)
7255 int nbNewNodes = quantities[iface];
7256 face_nodes.assign( polygons_nodes.begin() + inode,
7257 polygons_nodes.begin() + inode + nbNewNodes );
7258 inode += nbNewNodes;
7259 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7261 bool isValid = ( nbNewNodes % 2 == 0 );
7262 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7263 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7264 elemType.SetQuad( isValid );
7265 if ( isValid ) // put medium nodes after corners
7266 SMDS_MeshCell::applyInterlaceRev
7267 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7268 nbNewNodes ), face_nodes );
7270 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7272 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7274 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7277 rmElemIds.push_back(elem->GetID());
7281 else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7283 if ( nbUniqueNodes < 4 ) {
7284 rmElemIds.push_back(elem->GetID());
7287 // each face has to be analyzed in order to check volume validity
7288 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7291 int nbFaces = aPolyedre->NbFaces();
7293 vector<const SMDS_MeshNode *> poly_nodes;
7294 vector<int> quantities;
7295 vector<const SMDS_MeshNode *> faceNodes;
7297 for (int iface = 1; iface <= nbFaces; iface++)
7299 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7300 faceNodes.resize( nbFaceNodes );
7301 for (int inode = 1; inode <= nbFaceNodes; inode++)
7303 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7304 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7305 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7306 faceNode = (*nnIt).second;
7307 faceNodes[inode - 1] = faceNode;
7309 SimplifyFace(faceNodes, poly_nodes, quantities);
7312 if ( quantities.size() > 3 ) {
7313 // TODO: remove coincident faces
7316 if ( quantities.size() > 3 )
7318 const SMDS_MeshElement* newElem =
7319 aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7320 myLastCreatedElems.push_back( newElem );
7321 if ( aShapeId && newElem )
7322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7323 rmElemIds.push_back( elem->GetID() );
7327 rmElemIds.push_back( elem->GetID() );
7338 // TODO not all the possible cases are solved. Find something more generic?
7340 case SMDSEntity_Edge: //////// EDGE
7341 case SMDSEntity_Triangle: //// TRIANGLE
7342 case SMDSEntity_Quad_Triangle:
7343 case SMDSEntity_Tetra:
7344 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7349 case SMDSEntity_Quad_Edge:
7351 isOk = false; // to linear EDGE ???????
7354 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7356 if ( nbUniqueNodes < 3 )
7358 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7359 isOk = false; // opposite nodes stick
7362 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7371 if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7372 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7373 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7374 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7375 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7381 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7390 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7391 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7392 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7393 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7394 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7400 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7403 if ( nbUniqueNodes == 4 ) {
7404 // ---------------------------------> tetrahedron
7405 if ( curNodes[3] == curNodes[4] &&
7406 curNodes[3] == curNodes[5] ) {
7410 else if ( curNodes[0] == curNodes[1] &&
7411 curNodes[0] == curNodes[2] ) {
7412 // bottom nodes stick: set a top before
7413 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7414 uniqueNodes[ 0 ] = curNodes [ 5 ];
7415 uniqueNodes[ 1 ] = curNodes [ 4 ];
7416 uniqueNodes[ 2 ] = curNodes [ 3 ];
7419 else if (( curNodes[0] == curNodes[3] ) +
7420 ( curNodes[1] == curNodes[4] ) +
7421 ( curNodes[2] == curNodes[5] ) == 2 ) {
7422 // a lateral face turns into a line
7426 else if ( nbUniqueNodes == 5 ) {
7427 // PENTAHEDRON --------------------> pyramid
7428 if ( curNodes[0] == curNodes[3] )
7430 uniqueNodes[ 0 ] = curNodes[ 1 ];
7431 uniqueNodes[ 1 ] = curNodes[ 4 ];
7432 uniqueNodes[ 2 ] = curNodes[ 5 ];
7433 uniqueNodes[ 3 ] = curNodes[ 2 ];
7434 uniqueNodes[ 4 ] = curNodes[ 0 ];
7437 if ( curNodes[1] == curNodes[4] )
7439 uniqueNodes[ 0 ] = curNodes[ 0 ];
7440 uniqueNodes[ 1 ] = curNodes[ 2 ];
7441 uniqueNodes[ 2 ] = curNodes[ 5 ];
7442 uniqueNodes[ 3 ] = curNodes[ 3 ];
7443 uniqueNodes[ 4 ] = curNodes[ 1 ];
7446 if ( curNodes[2] == curNodes[5] )
7448 uniqueNodes[ 0 ] = curNodes[ 0 ];
7449 uniqueNodes[ 1 ] = curNodes[ 3 ];
7450 uniqueNodes[ 2 ] = curNodes[ 4 ];
7451 uniqueNodes[ 3 ] = curNodes[ 1 ];
7452 uniqueNodes[ 4 ] = curNodes[ 2 ];
7458 case SMDSEntity_Hexa:
7460 //////////////////////////////////// HEXAHEDRON
7462 SMDS_VolumeTool hexa (elem);
7463 hexa.SetExternalNormal();
7464 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7465 //////////////////////// HEX ---> tetrahedron
7466 for ( int iFace = 0; iFace < 6; iFace++ ) {
7467 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7468 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7469 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7470 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7471 // one face turns into a point ...
7472 int pickInd = ind[ 0 ];
7473 int iOppFace = hexa.GetOppFaceIndex( iFace );
7474 ind = hexa.GetFaceNodesIndices( iOppFace );
7476 uniqueNodes.clear();
7477 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7478 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7481 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7483 if ( nbStick == 1 ) {
7484 // ... and the opposite one - into a triangle.
7486 uniqueNodes.push_back( curNodes[ pickInd ]);
7493 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7494 //////////////////////// HEX ---> prism
7495 int nbTria = 0, iTria[3];
7496 const int *ind; // indices of face nodes
7497 // look for triangular faces
7498 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7499 ind = hexa.GetFaceNodesIndices( iFace );
7500 TIDSortedNodeSet faceNodes;
7501 for ( iCur = 0; iCur < 4; iCur++ )
7502 faceNodes.insert( curNodes[ind[iCur]] );
7503 if ( faceNodes.size() == 3 )
7504 iTria[ nbTria++ ] = iFace;
7506 // check if triangles are opposite
7507 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7509 // set nodes of the bottom triangle
7510 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7512 for ( iCur = 0; iCur < 4; iCur++ )
7513 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7514 indB.push_back( ind[iCur] );
7515 if ( !hexa.IsForward() )
7516 std::swap( indB[0], indB[2] );
7517 for ( iCur = 0; iCur < 3; iCur++ )
7518 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7519 // set nodes of the top triangle
7520 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7521 for ( iCur = 0; iCur < 3; ++iCur )
7522 for ( int j = 0; j < 4; ++j )
7523 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7525 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7532 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7533 //////////////////// HEXAHEDRON ---> pyramid
7534 for ( int iFace = 0; iFace < 6; iFace++ ) {
7535 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7536 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7537 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7538 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7539 // one face turns into a point ...
7540 int iOppFace = hexa.GetOppFaceIndex( iFace );
7541 ind = hexa.GetFaceNodesIndices( iOppFace );
7542 uniqueNodes.clear();
7543 for ( iCur = 0; iCur < 4; iCur++ ) {
7544 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7547 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7549 if ( uniqueNodes.size() == 4 ) {
7550 // ... and the opposite one is a quadrangle
7552 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7553 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7561 if ( !isOk && nbUniqueNodes > 4 ) {
7562 ////////////////// HEXAHEDRON ---> polyhedron
7563 hexa.SetExternalNormal();
7564 vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7565 vector<int> quantities; quantities.reserve( 6 );
7566 for ( int iFace = 0; iFace < 6; iFace++ )
7568 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7569 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7570 curNodes[ind[1]] == curNodes[ind[3]] )
7573 break; // opposite nodes stick
7576 for ( iCur = 0; iCur < 4; iCur++ )
7578 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7579 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7581 if ( nodeSet.size() < 3 )
7582 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7584 quantities.push_back( nodeSet.size() );
7586 if ( quantities.size() >= 4 )
7588 const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7589 myLastCreatedElems.push_back( newElem );
7590 if ( aShapeId && newElem )
7591 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7592 rmElemIds.push_back( elem->GetID() );
7596 } // case HEXAHEDRON
7600 } // switch ( nbNodes )
7602 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7604 if ( isOk ) // a non-poly elem remains valid after sticking nodes
7606 if ( nbNodes != nbUniqueNodes ||
7607 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7609 elemType.Init( elem ).SetID( elem->GetID() );
7611 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7612 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7614 uniqueNodes.resize(nbUniqueNodes);
7615 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7616 if ( sm && newElem )
7617 sm->AddElement( newElem );
7618 if ( elem != newElem )
7619 ReplaceElemInGroups( elem, newElem, aMesh );
7623 // Remove invalid regular element or invalid polygon
7624 rmElemIds.push_back( elem->GetID() );
7627 } // loop on elements
7629 // Remove bad elements, then equal nodes (order important)
7631 Remove( rmElemIds, false );
7632 Remove( rmNodeIds, true );
7638 // ========================================================
7639 // class : SortableElement
7640 // purpose : allow sorting elements basing on their nodes
7641 // ========================================================
7642 class SortableElement : public set <const SMDS_MeshElement*>
7646 SortableElement( const SMDS_MeshElement* theElem )
7649 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7650 while ( nodeIt->more() )
7651 this->insert( nodeIt->next() );
7654 const SMDS_MeshElement* Get() const
7658 mutable const SMDS_MeshElement* myElem;
7661 //=======================================================================
7662 //function : FindEqualElements
7663 //purpose : Return list of group of elements built on the same nodes.
7664 // Search among theElements or in the whole mesh if theElements is empty
7665 //=======================================================================
7667 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7668 TListOfListOfElementsID & theGroupsOfElementsID)
7672 typedef map< SortableElement, int > TMapOfNodeSet;
7673 typedef list<int> TGroupOfElems;
7675 SMDS_ElemIteratorPtr elemIt;
7676 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7677 else elemIt = SMESHUtils::elemSetIterator( theElements );
7679 vector< TGroupOfElems > arrayOfGroups;
7680 TGroupOfElems groupOfElems;
7681 TMapOfNodeSet mapOfNodeSet;
7683 for ( int i = 0; elemIt->more(); )
7685 const SMDS_MeshElement* curElem = elemIt->next();
7686 SortableElement SE(curElem);
7688 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7689 if ( !pp.second ) { // one more coincident elem
7690 TMapOfNodeSet::iterator& itSE = pp.first;
7691 int ind = (*itSE).second;
7692 arrayOfGroups[ind].push_back( curElem->GetID() );
7695 arrayOfGroups.push_back( groupOfElems );
7696 arrayOfGroups.back().push_back( curElem->GetID() );
7701 groupOfElems.clear();
7702 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7703 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7705 if ( groupIt->size() > 1 ) {
7706 //groupOfElems.sort(); -- theElements is sorted already
7707 theGroupsOfElementsID.push_back( groupOfElems );
7708 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7713 //=======================================================================
7714 //function : MergeElements
7715 //purpose : In each given group, substitute all elements by the first one.
7716 //=======================================================================
7718 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7722 typedef list<int> TListOfIDs;
7723 TListOfIDs rmElemIds; // IDs of elems to remove
7725 SMESHDS_Mesh* aMesh = GetMeshDS();
7727 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7728 while ( groupsIt != theGroupsOfElementsID.end() ) {
7729 TListOfIDs& aGroupOfElemID = *groupsIt;
7730 aGroupOfElemID.sort();
7731 int elemIDToKeep = aGroupOfElemID.front();
7732 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7733 aGroupOfElemID.pop_front();
7734 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7735 while ( idIt != aGroupOfElemID.end() ) {
7736 int elemIDToRemove = *idIt;
7737 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7738 // add the kept element in groups of removed one (PAL15188)
7739 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7740 rmElemIds.push_back( elemIDToRemove );
7746 Remove( rmElemIds, false );
7749 //=======================================================================
7750 //function : MergeEqualElements
7751 //purpose : Remove all but one of elements built on the same nodes.
7752 //=======================================================================
7754 void SMESH_MeshEditor::MergeEqualElements()
7756 TIDSortedElemSet aMeshElements; /* empty input ==
7757 to merge equal elements in the whole mesh */
7758 TListOfListOfElementsID aGroupsOfElementsID;
7759 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7760 MergeElements(aGroupsOfElementsID);
7763 //=======================================================================
7764 //function : findAdjacentFace
7766 //=======================================================================
7768 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7769 const SMDS_MeshNode* n2,
7770 const SMDS_MeshElement* elem)
7772 TIDSortedElemSet elemSet, avoidSet;
7774 avoidSet.insert ( elem );
7775 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7778 //=======================================================================
7779 //function : findSegment
7780 //purpose : Return a mesh segment by two nodes one of which can be medium
7781 //=======================================================================
7783 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7784 const SMDS_MeshNode* n2)
7786 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7787 while ( it->more() )
7789 const SMDS_MeshElement* seg = it->next();
7790 if ( seg->GetNodeIndex( n2 ) >= 0 )
7796 //=======================================================================
7797 //function : FindFreeBorder
7799 //=======================================================================
7801 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7803 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7804 const SMDS_MeshNode* theSecondNode,
7805 const SMDS_MeshNode* theLastNode,
7806 list< const SMDS_MeshNode* > & theNodes,
7807 list< const SMDS_MeshElement* >& theFaces)
7809 if ( !theFirstNode || !theSecondNode )
7811 // find border face between theFirstNode and theSecondNode
7812 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7816 theFaces.push_back( curElem );
7817 theNodes.push_back( theFirstNode );
7818 theNodes.push_back( theSecondNode );
7820 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7821 TIDSortedElemSet foundElems;
7822 bool needTheLast = ( theLastNode != 0 );
7824 while ( nStart != theLastNode ) {
7825 if ( nStart == theFirstNode )
7826 return !needTheLast;
7828 // find all free border faces sharing form nStart
7830 list< const SMDS_MeshElement* > curElemList;
7831 list< const SMDS_MeshNode* > nStartList;
7832 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7833 while ( invElemIt->more() ) {
7834 const SMDS_MeshElement* e = invElemIt->next();
7835 if ( e == curElem || foundElems.insert( e ).second ) {
7837 int iNode = 0, nbNodes = e->NbNodes();
7838 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7840 if ( e->IsQuadratic() ) {
7841 const SMDS_VtkFace* F =
7842 dynamic_cast<const SMDS_VtkFace*>(e);
7843 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7844 // use special nodes iterator
7845 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7846 while( anIter->more() ) {
7847 nodes[ iNode++ ] = cast2Node(anIter->next());
7851 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7852 while ( nIt->more() )
7853 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7855 nodes[ iNode ] = nodes[ 0 ];
7857 for ( iNode = 0; iNode < nbNodes; iNode++ )
7858 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7859 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7860 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7862 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7863 curElemList.push_back( e );
7867 // analyse the found
7869 int nbNewBorders = curElemList.size();
7870 if ( nbNewBorders == 0 ) {
7871 // no free border furthermore
7872 return !needTheLast;
7874 else if ( nbNewBorders == 1 ) {
7875 // one more element found
7877 nStart = nStartList.front();
7878 curElem = curElemList.front();
7879 theFaces.push_back( curElem );
7880 theNodes.push_back( nStart );
7883 // several continuations found
7884 list< const SMDS_MeshElement* >::iterator curElemIt;
7885 list< const SMDS_MeshNode* >::iterator nStartIt;
7886 // check if one of them reached the last node
7887 if ( needTheLast ) {
7888 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7889 curElemIt!= curElemList.end();
7890 curElemIt++, nStartIt++ )
7891 if ( *nStartIt == theLastNode ) {
7892 theFaces.push_back( *curElemIt );
7893 theNodes.push_back( *nStartIt );
7897 // find the best free border by the continuations
7898 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7899 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7900 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7901 curElemIt!= curElemList.end();
7902 curElemIt++, nStartIt++ )
7904 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7905 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7906 // find one more free border
7907 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7911 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7912 // choice: clear a worse one
7913 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7914 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7915 contNodes[ iWorse ].clear();
7916 contFaces[ iWorse ].clear();
7919 if ( contNodes[0].empty() && contNodes[1].empty() )
7922 // push_back the best free border
7923 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7924 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7925 theNodes.pop_back(); // remove nIgnore
7926 theNodes.pop_back(); // remove nStart
7927 theFaces.pop_back(); // remove curElem
7928 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7929 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7930 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7931 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7934 } // several continuations found
7935 } // while ( nStart != theLastNode )
7940 //=======================================================================
7941 //function : CheckFreeBorderNodes
7942 //purpose : Return true if the tree nodes are on a free border
7943 //=======================================================================
7945 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7946 const SMDS_MeshNode* theNode2,
7947 const SMDS_MeshNode* theNode3)
7949 list< const SMDS_MeshNode* > nodes;
7950 list< const SMDS_MeshElement* > faces;
7951 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7954 //=======================================================================
7955 //function : SewFreeBorder
7957 //warning : for border-to-side sewing theSideSecondNode is considered as
7958 // the last side node and theSideThirdNode is not used
7959 //=======================================================================
7961 SMESH_MeshEditor::Sew_Error
7962 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7963 const SMDS_MeshNode* theBordSecondNode,
7964 const SMDS_MeshNode* theBordLastNode,
7965 const SMDS_MeshNode* theSideFirstNode,
7966 const SMDS_MeshNode* theSideSecondNode,
7967 const SMDS_MeshNode* theSideThirdNode,
7968 const bool theSideIsFreeBorder,
7969 const bool toCreatePolygons,
7970 const bool toCreatePolyedrs)
7974 Sew_Error aResult = SEW_OK;
7976 // ====================================
7977 // find side nodes and elements
7978 // ====================================
7980 list< const SMDS_MeshNode* > nSide[ 2 ];
7981 list< const SMDS_MeshElement* > eSide[ 2 ];
7982 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7983 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7987 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7988 nSide[0], eSide[0])) {
7989 MESSAGE(" Free Border 1 not found " );
7990 aResult = SEW_BORDER1_NOT_FOUND;
7992 if (theSideIsFreeBorder) {
7995 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7996 nSide[1], eSide[1])) {
7997 MESSAGE(" Free Border 2 not found " );
7998 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8001 if ( aResult != SEW_OK )
8004 if (!theSideIsFreeBorder) {
8008 // -------------------------------------------------------------------------
8010 // 1. If nodes to merge are not coincident, move nodes of the free border
8011 // from the coord sys defined by the direction from the first to last
8012 // nodes of the border to the correspondent sys of the side 2
8013 // 2. On the side 2, find the links most co-directed with the correspondent
8014 // links of the free border
8015 // -------------------------------------------------------------------------
8017 // 1. Since sewing may break if there are volumes to split on the side 2,
8018 // we wont move nodes but just compute new coordinates for them
8019 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8020 TNodeXYZMap nBordXYZ;
8021 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8022 list< const SMDS_MeshNode* >::iterator nBordIt;
8024 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8025 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8026 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8027 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8028 double tol2 = 1.e-8;
8029 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8030 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8031 // Need node movement.
8033 // find X and Z axes to create trsf
8034 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8036 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8038 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8041 gp_Ax3 toBordAx( Pb1, Zb, X );
8042 gp_Ax3 fromSideAx( Ps1, Zs, X );
8043 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8045 gp_Trsf toBordSys, fromSide2Sys;
8046 toBordSys.SetTransformation( toBordAx );
8047 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8048 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8051 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8052 const SMDS_MeshNode* n = *nBordIt;
8053 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8054 toBordSys.Transforms( xyz );
8055 fromSide2Sys.Transforms( xyz );
8056 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8060 // just insert nodes XYZ in the nBordXYZ map
8061 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8062 const SMDS_MeshNode* n = *nBordIt;
8063 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8067 // 2. On the side 2, find the links most co-directed with the correspondent
8068 // links of the free border
8070 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8071 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8072 sideNodes.push_back( theSideFirstNode );
8074 bool hasVolumes = false;
8075 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8076 set<long> foundSideLinkIDs, checkedLinkIDs;
8077 SMDS_VolumeTool volume;
8078 //const SMDS_MeshNode* faceNodes[ 4 ];
8080 const SMDS_MeshNode* sideNode;
8081 const SMDS_MeshElement* sideElem = 0;
8082 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8083 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8084 nBordIt = bordNodes.begin();
8086 // border node position and border link direction to compare with
8087 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8088 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8089 // choose next side node by link direction or by closeness to
8090 // the current border node:
8091 bool searchByDir = ( *nBordIt != theBordLastNode );
8093 // find the next node on the Side 2
8095 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8097 checkedLinkIDs.clear();
8098 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8100 // loop on inverse elements of current node (prevSideNode) on the Side 2
8101 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8102 while ( invElemIt->more() )
8104 const SMDS_MeshElement* elem = invElemIt->next();
8105 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8106 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8107 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8108 bool isVolume = volume.Set( elem );
8109 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8110 if ( isVolume ) // --volume
8112 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8113 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8114 if(elem->IsQuadratic()) {
8115 const SMDS_VtkFace* F =
8116 dynamic_cast<const SMDS_VtkFace*>(elem);
8117 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8118 // use special nodes iterator
8119 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8120 while( anIter->more() ) {
8121 nodes[ iNode ] = cast2Node(anIter->next());
8122 if ( nodes[ iNode++ ] == prevSideNode )
8123 iPrevNode = iNode - 1;
8127 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8128 while ( nIt->more() ) {
8129 nodes[ iNode ] = cast2Node( nIt->next() );
8130 if ( nodes[ iNode++ ] == prevSideNode )
8131 iPrevNode = iNode - 1;
8134 // there are 2 links to check
8139 // loop on links, to be precise, on the second node of links
8140 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8141 const SMDS_MeshNode* n = nodes[ iNode ];
8143 if ( !volume.IsLinked( n, prevSideNode ))
8147 if ( iNode ) // a node before prevSideNode
8148 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8149 else // a node after prevSideNode
8150 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8152 // check if this link was already used
8153 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8154 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8155 if (!isJustChecked &&
8156 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8158 // test a link geometrically
8159 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8160 bool linkIsBetter = false;
8161 double dot = 0.0, dist = 0.0;
8162 if ( searchByDir ) { // choose most co-directed link
8163 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8164 linkIsBetter = ( dot > maxDot );
8166 else { // choose link with the node closest to bordPos
8167 dist = ( nextXYZ - bordPos ).SquareModulus();
8168 linkIsBetter = ( dist < minDist );
8170 if ( linkIsBetter ) {
8179 } // loop on inverse elements of prevSideNode
8182 MESSAGE(" Cant find path by links of the Side 2 ");
8183 return SEW_BAD_SIDE_NODES;
8185 sideNodes.push_back( sideNode );
8186 sideElems.push_back( sideElem );
8187 foundSideLinkIDs.insert ( linkID );
8188 prevSideNode = sideNode;
8190 if ( *nBordIt == theBordLastNode )
8191 searchByDir = false;
8193 // find the next border link to compare with
8194 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8195 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8196 // move to next border node if sideNode is before forward border node (bordPos)
8197 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8198 prevBordNode = *nBordIt;
8200 bordPos = nBordXYZ[ *nBordIt ];
8201 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8202 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8206 while ( sideNode != theSideSecondNode );
8208 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8209 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8210 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8212 } // end nodes search on the side 2
8214 // ============================
8215 // sew the border to the side 2
8216 // ============================
8218 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8219 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8221 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8222 if ( toMergeConformal && toCreatePolygons )
8224 // do not merge quadrangles if polygons are OK (IPAL0052824)
8225 eIt[0] = eSide[0].begin();
8226 eIt[1] = eSide[1].begin();
8227 bool allQuads[2] = { true, true };
8228 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8229 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8230 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8232 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8235 TListOfListOfNodes nodeGroupsToMerge;
8236 if (( toMergeConformal ) ||
8237 ( theSideIsFreeBorder && !theSideThirdNode )) {
8239 // all nodes are to be merged
8241 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8242 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8243 nIt[0]++, nIt[1]++ )
8245 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8246 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8247 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8252 // insert new nodes into the border and the side to get equal nb of segments
8254 // get normalized parameters of nodes on the borders
8255 vector< double > param[ 2 ];
8256 param[0].resize( maxNbNodes );
8257 param[1].resize( maxNbNodes );
8259 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8260 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8261 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8262 const SMDS_MeshNode* nPrev = *nIt;
8263 double bordLength = 0;
8264 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8265 const SMDS_MeshNode* nCur = *nIt;
8266 gp_XYZ segment (nCur->X() - nPrev->X(),
8267 nCur->Y() - nPrev->Y(),
8268 nCur->Z() - nPrev->Z());
8269 double segmentLen = segment.Modulus();
8270 bordLength += segmentLen;
8271 param[ iBord ][ iNode ] = bordLength;
8274 // normalize within [0,1]
8275 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8276 param[ iBord ][ iNode ] /= bordLength;
8280 // loop on border segments
8281 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8282 int i[ 2 ] = { 0, 0 };
8283 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8284 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8286 TElemOfNodeListMap insertMap;
8287 TElemOfNodeListMap::iterator insertMapIt;
8289 // key: elem to insert nodes into
8290 // value: 2 nodes to insert between + nodes to be inserted
8292 bool next[ 2 ] = { false, false };
8294 // find min adjacent segment length after sewing
8295 double nextParam = 10., prevParam = 0;
8296 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8297 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8298 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8299 if ( i[ iBord ] > 0 )
8300 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8302 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8303 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8304 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8306 // choose to insert or to merge nodes
8307 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8308 if ( Abs( du ) <= minSegLen * 0.2 ) {
8311 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8312 const SMDS_MeshNode* n0 = *nIt[0];
8313 const SMDS_MeshNode* n1 = *nIt[1];
8314 nodeGroupsToMerge.back().push_back( n1 );
8315 nodeGroupsToMerge.back().push_back( n0 );
8316 // position of node of the border changes due to merge
8317 param[ 0 ][ i[0] ] += du;
8318 // move n1 for the sake of elem shape evaluation during insertion.
8319 // n1 will be removed by MergeNodes() anyway
8320 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8321 next[0] = next[1] = true;
8326 int intoBord = ( du < 0 ) ? 0 : 1;
8327 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8328 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8329 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8330 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8331 if ( intoBord == 1 ) {
8332 // move node of the border to be on a link of elem of the side
8333 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8334 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8335 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8336 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8337 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8339 insertMapIt = insertMap.find( elem );
8340 bool notFound = ( insertMapIt == insertMap.end() );
8341 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8343 // insert into another link of the same element:
8344 // 1. perform insertion into the other link of the elem
8345 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8346 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8347 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8348 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8349 // 2. perform insertion into the link of adjacent faces
8350 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8351 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8353 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8354 InsertNodesIntoLink( seg, n12, n22, nodeList );
8356 if (toCreatePolyedrs) {
8357 // perform insertion into the links of adjacent volumes
8358 UpdateVolumes(n12, n22, nodeList);
8360 // 3. find an element appeared on n1 and n2 after the insertion
8361 insertMap.erase( elem );
8362 elem = findAdjacentFace( n1, n2, 0 );
8364 if ( notFound || otherLink ) {
8365 // add element and nodes of the side into the insertMap
8366 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8367 (*insertMapIt).second.push_back( n1 );
8368 (*insertMapIt).second.push_back( n2 );
8370 // add node to be inserted into elem
8371 (*insertMapIt).second.push_back( nIns );
8372 next[ 1 - intoBord ] = true;
8375 // go to the next segment
8376 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8377 if ( next[ iBord ] ) {
8378 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8380 nPrev[ iBord ] = *nIt[ iBord ];
8381 nIt[ iBord ]++; i[ iBord ]++;
8385 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8387 // perform insertion of nodes into elements
8389 for (insertMapIt = insertMap.begin();
8390 insertMapIt != insertMap.end();
8393 const SMDS_MeshElement* elem = (*insertMapIt).first;
8394 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8395 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8396 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8398 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8400 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8401 InsertNodesIntoLink( seg, n1, n2, nodeList );
8404 if ( !theSideIsFreeBorder ) {
8405 // look for and insert nodes into the faces adjacent to elem
8406 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8407 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8410 if (toCreatePolyedrs) {
8411 // perform insertion into the links of adjacent volumes
8412 UpdateVolumes(n1, n2, nodeList);
8415 } // end: insert new nodes
8417 MergeNodes ( nodeGroupsToMerge );
8420 // Remove coincident segments
8423 TIDSortedElemSet segments;
8424 SMESH_SequenceOfElemPtr newFaces;
8425 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8427 if ( !myLastCreatedElems[i] ) continue;
8428 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8429 segments.insert( segments.end(), myLastCreatedElems[i] );
8431 newFaces.push_back( myLastCreatedElems[i] );
8433 // get segments adjacent to merged nodes
8434 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8435 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8437 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8438 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8439 while ( segIt->more() )
8440 segments.insert( segIt->next() );
8444 TListOfListOfElementsID equalGroups;
8445 if ( !segments.empty() )
8446 FindEqualElements( segments, equalGroups );
8447 if ( !equalGroups.empty() )
8449 // remove from segments those that will be removed
8450 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8451 for ( ; itGroups != equalGroups.end(); ++itGroups )
8453 list< int >& group = *itGroups;
8454 list< int >::iterator id = group.begin();
8455 for ( ++id; id != group.end(); ++id )
8456 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8457 segments.erase( seg );
8459 // remove equal segments
8460 MergeElements( equalGroups );
8462 // restore myLastCreatedElems
8463 myLastCreatedElems = newFaces;
8464 TIDSortedElemSet::iterator seg = segments.begin();
8465 for ( ; seg != segments.end(); ++seg )
8466 myLastCreatedElems.push_back( *seg );
8472 //=======================================================================
8473 //function : InsertNodesIntoLink
8474 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8475 // and theBetweenNode2 and split theElement
8476 //=======================================================================
8478 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8479 const SMDS_MeshNode* theBetweenNode1,
8480 const SMDS_MeshNode* theBetweenNode2,
8481 list<const SMDS_MeshNode*>& theNodesToInsert,
8482 const bool toCreatePoly)
8484 if ( !theElement ) return;
8486 SMESHDS_Mesh *aMesh = GetMeshDS();
8487 vector<const SMDS_MeshElement*> newElems;
8489 if ( theElement->GetType() == SMDSAbs_Edge )
8491 theNodesToInsert.push_front( theBetweenNode1 );
8492 theNodesToInsert.push_back ( theBetweenNode2 );
8493 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8494 const SMDS_MeshNode* n1 = *n;
8495 for ( ++n; n != theNodesToInsert.end(); ++n )
8497 const SMDS_MeshNode* n2 = *n;
8498 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8499 AddToSameGroups( seg, theElement, aMesh );
8501 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8504 theNodesToInsert.pop_front();
8505 theNodesToInsert.pop_back();
8507 if ( theElement->IsQuadratic() ) // add a not split part
8509 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8510 theElement->end_nodes() );
8511 int iOther = 0, nbN = nodes.size();
8512 for ( ; iOther < nbN; ++iOther )
8513 if ( nodes[iOther] != theBetweenNode1 &&
8514 nodes[iOther] != theBetweenNode2 )
8518 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8519 AddToSameGroups( seg, theElement, aMesh );
8521 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8523 else if ( iOther == 2 )
8525 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8526 AddToSameGroups( seg, theElement, aMesh );
8528 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8531 // treat new elements
8532 for ( size_t i = 0; i < newElems.size(); ++i )
8535 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8536 myLastCreatedElems.push_back( newElems[i] );
8538 ReplaceElemInGroups( theElement, newElems, aMesh );
8539 aMesh->RemoveElement( theElement );
8542 } // if ( theElement->GetType() == SMDSAbs_Edge )
8544 const SMDS_MeshElement* theFace = theElement;
8545 if ( theFace->GetType() != SMDSAbs_Face ) return;
8547 // find indices of 2 link nodes and of the rest nodes
8548 int iNode = 0, il1, il2, i3, i4;
8549 il1 = il2 = i3 = i4 = -1;
8550 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8552 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8553 while ( nodeIt->more() ) {
8554 const SMDS_MeshNode* n = nodeIt->next();
8555 if ( n == theBetweenNode1 )
8557 else if ( n == theBetweenNode2 )
8563 nodes[ iNode++ ] = n;
8565 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8568 // arrange link nodes to go one after another regarding the face orientation
8569 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8570 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8575 aNodesToInsert.reverse();
8577 // check that not link nodes of a quadrangles are in good order
8578 int nbFaceNodes = theFace->NbNodes();
8579 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8585 if (toCreatePoly || theFace->IsPoly()) {
8588 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8590 // add nodes of face up to first node of link
8593 if ( theFace->IsQuadratic() ) {
8594 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8595 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8596 // use special nodes iterator
8597 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8598 while( anIter->more() && !isFLN ) {
8599 const SMDS_MeshNode* n = cast2Node(anIter->next());
8600 poly_nodes[iNode++] = n;
8601 if (n == nodes[il1]) {
8605 // add nodes to insert
8606 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8607 for (; nIt != aNodesToInsert.end(); nIt++) {
8608 poly_nodes[iNode++] = *nIt;
8610 // add nodes of face starting from last node of link
8611 while ( anIter->more() ) {
8612 poly_nodes[iNode++] = cast2Node(anIter->next());
8616 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8617 while ( nodeIt->more() && !isFLN ) {
8618 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8619 poly_nodes[iNode++] = n;
8620 if (n == nodes[il1]) {
8624 // add nodes to insert
8625 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8626 for (; nIt != aNodesToInsert.end(); nIt++) {
8627 poly_nodes[iNode++] = *nIt;
8629 // add nodes of face starting from last node of link
8630 while ( nodeIt->more() ) {
8631 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8632 poly_nodes[iNode++] = n;
8637 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8640 else if ( !theFace->IsQuadratic() )
8642 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8643 int nbLinkNodes = 2 + aNodesToInsert.size();
8644 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8645 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8646 linkNodes[ 0 ] = nodes[ il1 ];
8647 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8648 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8649 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8650 linkNodes[ iNode++ ] = *nIt;
8652 // decide how to split a quadrangle: compare possible variants
8653 // and choose which of splits to be a quadrangle
8654 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8655 if ( nbFaceNodes == 3 ) {
8656 iBestQuad = nbSplits;
8659 else if ( nbFaceNodes == 4 ) {
8660 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8661 double aBestRate = DBL_MAX;
8662 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8664 double aBadRate = 0;
8665 // evaluate elements quality
8666 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8667 if ( iSplit == iQuad ) {
8668 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8672 aBadRate += getBadRate( &quad, aCrit );
8675 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8677 nodes[ iSplit < iQuad ? i4 : i3 ]);
8678 aBadRate += getBadRate( &tria, aCrit );
8682 if ( aBadRate < aBestRate ) {
8684 aBestRate = aBadRate;
8689 // create new elements
8691 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8693 if ( iSplit == iBestQuad )
8694 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8699 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8701 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8704 const SMDS_MeshNode* newNodes[ 4 ];
8705 newNodes[ 0 ] = linkNodes[ i1 ];
8706 newNodes[ 1 ] = linkNodes[ i2 ];
8707 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8708 newNodes[ 3 ] = nodes[ i4 ];
8709 if (iSplit == iBestQuad)
8710 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8712 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8714 } // end if(!theFace->IsQuadratic())
8716 else { // theFace is quadratic
8717 // we have to split theFace on simple triangles and one simple quadrangle
8719 int nbshift = tmp*2;
8720 // shift nodes in nodes[] by nbshift
8722 for(i=0; i<nbshift; i++) {
8723 const SMDS_MeshNode* n = nodes[0];
8724 for(j=0; j<nbFaceNodes-1; j++) {
8725 nodes[j] = nodes[j+1];
8727 nodes[nbFaceNodes-1] = n;
8729 il1 = il1 - nbshift;
8730 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8731 // n0 n1 n2 n0 n1 n2
8732 // +-----+-----+ +-----+-----+
8741 // create new elements
8743 if ( nbFaceNodes == 6 ) { // quadratic triangle
8744 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8745 if ( theFace->IsMediumNode(nodes[il1]) ) {
8746 // create quadrangle
8747 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8753 // create quadrangle
8754 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8760 else { // nbFaceNodes==8 - quadratic quadrangle
8761 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8762 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8763 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8764 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8765 // create quadrangle
8766 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8772 // create quadrangle
8773 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8779 // create needed triangles using n1,n2,n3 and inserted nodes
8780 int nbn = 2 + aNodesToInsert.size();
8781 vector<const SMDS_MeshNode*> aNodes(nbn);
8782 aNodes[0 ] = nodes[n1];
8783 aNodes[nbn-1] = nodes[n2];
8784 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8785 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8786 aNodes[iNode++] = *nIt;
8788 for ( i = 1; i < nbn; i++ )
8789 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8792 // remove the old face
8793 for ( size_t i = 0; i < newElems.size(); ++i )
8796 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8797 myLastCreatedElems.push_back( newElems[i] );
8799 ReplaceElemInGroups( theFace, newElems, aMesh );
8800 aMesh->RemoveElement(theFace);
8802 } // InsertNodesIntoLink()
8804 //=======================================================================
8805 //function : UpdateVolumes
8807 //=======================================================================
8809 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8810 const SMDS_MeshNode* theBetweenNode2,
8811 list<const SMDS_MeshNode*>& theNodesToInsert)
8815 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8816 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8817 const SMDS_MeshElement* elem = invElemIt->next();
8819 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8820 SMDS_VolumeTool aVolume (elem);
8821 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8824 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8825 int iface, nbFaces = aVolume.NbFaces();
8826 vector<const SMDS_MeshNode *> poly_nodes;
8827 vector<int> quantities (nbFaces);
8829 for (iface = 0; iface < nbFaces; iface++) {
8830 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8831 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8832 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8834 for (int inode = 0; inode < nbFaceNodes; inode++) {
8835 poly_nodes.push_back(faceNodes[inode]);
8837 if (nbInserted == 0) {
8838 if (faceNodes[inode] == theBetweenNode1) {
8839 if (faceNodes[inode + 1] == theBetweenNode2) {
8840 nbInserted = theNodesToInsert.size();
8842 // add nodes to insert
8843 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8844 for (; nIt != theNodesToInsert.end(); nIt++) {
8845 poly_nodes.push_back(*nIt);
8849 else if (faceNodes[inode] == theBetweenNode2) {
8850 if (faceNodes[inode + 1] == theBetweenNode1) {
8851 nbInserted = theNodesToInsert.size();
8853 // add nodes to insert in reversed order
8854 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8856 for (; nIt != theNodesToInsert.begin(); nIt--) {
8857 poly_nodes.push_back(*nIt);
8859 poly_nodes.push_back(*nIt);
8866 quantities[iface] = nbFaceNodes + nbInserted;
8869 // Replace the volume
8870 SMESHDS_Mesh *aMesh = GetMeshDS();
8872 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8874 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8875 myLastCreatedElems.push_back( newElem );
8876 ReplaceElemInGroups( elem, newElem, aMesh );
8878 aMesh->RemoveElement( elem );
8884 //================================================================================
8886 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8888 //================================================================================
8890 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8891 vector<const SMDS_MeshNode *> & nodes,
8892 vector<int> & nbNodeInFaces )
8895 nbNodeInFaces.clear();
8896 SMDS_VolumeTool vTool ( elem );
8897 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8899 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8900 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8901 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8906 //=======================================================================
8908 * \brief Convert elements contained in a sub-mesh to quadratic
8909 * \return int - nb of checked elements
8911 //=======================================================================
8913 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8914 SMESH_MesherHelper& theHelper,
8915 const bool theForce3d)
8918 if( !theSm ) return nbElem;
8920 vector<int> nbNodeInFaces;
8921 vector<const SMDS_MeshNode *> nodes;
8922 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8923 while(ElemItr->more())
8926 const SMDS_MeshElement* elem = ElemItr->next();
8927 if( !elem ) continue;
8929 // analyse a necessity of conversion
8930 const SMDSAbs_ElementType aType = elem->GetType();
8931 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8933 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8934 bool hasCentralNodes = false;
8935 if ( elem->IsQuadratic() )
8938 switch ( aGeomType ) {
8939 case SMDSEntity_Quad_Triangle:
8940 case SMDSEntity_Quad_Quadrangle:
8941 case SMDSEntity_Quad_Hexa:
8942 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8944 case SMDSEntity_BiQuad_Triangle:
8945 case SMDSEntity_BiQuad_Quadrangle:
8946 case SMDSEntity_TriQuad_Hexa:
8947 alreadyOK = theHelper.GetIsBiQuadratic();
8948 hasCentralNodes = true;
8953 // take into account already present modium nodes
8955 case SMDSAbs_Volume:
8956 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8958 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8960 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8966 // get elem data needed to re-create it
8968 const int id = elem->GetID();
8969 const int nbNodes = elem->NbCornerNodes();
8970 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8971 if ( aGeomType == SMDSEntity_Polyhedra )
8972 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8973 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8974 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8976 // remove a linear element
8977 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8979 // remove central nodes of biquadratic elements (biquad->quad convertion)
8980 if ( hasCentralNodes )
8981 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8982 if ( nodes[i]->NbInverseElements() == 0 )
8983 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8985 const SMDS_MeshElement* NewElem = 0;
8991 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8999 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9002 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9005 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9009 case SMDSAbs_Volume :
9013 case SMDSEntity_Tetra:
9014 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9016 case SMDSEntity_Pyramid:
9017 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9019 case SMDSEntity_Penta:
9020 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9022 case SMDSEntity_Hexa:
9023 case SMDSEntity_Quad_Hexa:
9024 case SMDSEntity_TriQuad_Hexa:
9025 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9026 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9028 case SMDSEntity_Hexagonal_Prism:
9030 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9037 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9038 if( NewElem && NewElem->getshapeId() < 1 )
9039 theSm->AddElement( NewElem );
9043 //=======================================================================
9044 //function : ConvertToQuadratic
9046 //=======================================================================
9048 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9050 SMESHDS_Mesh* meshDS = GetMeshDS();
9052 SMESH_MesherHelper aHelper(*myMesh);
9054 aHelper.SetIsQuadratic( true );
9055 aHelper.SetIsBiQuadratic( theToBiQuad );
9056 aHelper.SetElementsOnShape(true);
9057 aHelper.ToFixNodeParameters( true );
9059 // convert elements assigned to sub-meshes
9060 int nbCheckedElems = 0;
9061 if ( myMesh->HasShapeToMesh() )
9063 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9065 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9066 while ( smIt->more() ) {
9067 SMESH_subMesh* sm = smIt->next();
9068 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9069 aHelper.SetSubShape( sm->GetSubShape() );
9070 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9076 // convert elements NOT assigned to sub-meshes
9077 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9078 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9080 aHelper.SetElementsOnShape(false);
9081 SMESHDS_SubMesh *smDS = 0;
9084 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9085 while( aEdgeItr->more() )
9087 const SMDS_MeshEdge* edge = aEdgeItr->next();
9088 if ( !edge->IsQuadratic() )
9090 int id = edge->GetID();
9091 const SMDS_MeshNode* n1 = edge->GetNode(0);
9092 const SMDS_MeshNode* n2 = edge->GetNode(1);
9094 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9096 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9097 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9101 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9106 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9107 while( aFaceItr->more() )
9109 const SMDS_MeshFace* face = aFaceItr->next();
9110 if ( !face ) continue;
9112 const SMDSAbs_EntityType type = face->GetEntityType();
9116 case SMDSEntity_Quad_Triangle:
9117 case SMDSEntity_Quad_Quadrangle:
9118 alreadyOK = !theToBiQuad;
9119 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9121 case SMDSEntity_BiQuad_Triangle:
9122 case SMDSEntity_BiQuad_Quadrangle:
9123 alreadyOK = theToBiQuad;
9124 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9126 default: alreadyOK = false;
9131 const int id = face->GetID();
9132 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9134 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9136 SMDS_MeshFace * NewFace = 0;
9139 case SMDSEntity_Triangle:
9140 case SMDSEntity_Quad_Triangle:
9141 case SMDSEntity_BiQuad_Triangle:
9142 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9143 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9144 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9147 case SMDSEntity_Quadrangle:
9148 case SMDSEntity_Quad_Quadrangle:
9149 case SMDSEntity_BiQuad_Quadrangle:
9150 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9151 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9152 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9156 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9158 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9162 vector<int> nbNodeInFaces;
9163 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9164 while(aVolumeItr->more())
9166 const SMDS_MeshVolume* volume = aVolumeItr->next();
9167 if ( !volume ) continue;
9169 const SMDSAbs_EntityType type = volume->GetEntityType();
9170 if ( volume->IsQuadratic() )
9175 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9176 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9177 default: alreadyOK = true;
9181 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9185 const int id = volume->GetID();
9186 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9187 if ( type == SMDSEntity_Polyhedra )
9188 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9189 else if ( type == SMDSEntity_Hexagonal_Prism )
9190 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9192 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9194 SMDS_MeshVolume * NewVolume = 0;
9197 case SMDSEntity_Tetra:
9198 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9200 case SMDSEntity_Hexa:
9201 case SMDSEntity_Quad_Hexa:
9202 case SMDSEntity_TriQuad_Hexa:
9203 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9204 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9205 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9206 if ( nodes[i]->NbInverseElements() == 0 )
9207 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9209 case SMDSEntity_Pyramid:
9210 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9211 nodes[3], nodes[4], id, theForce3d);
9213 case SMDSEntity_Penta:
9214 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9215 nodes[3], nodes[4], nodes[5], id, theForce3d);
9217 case SMDSEntity_Hexagonal_Prism:
9219 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9221 ReplaceElemInGroups(volume, NewVolume, meshDS);
9226 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9227 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9228 // aHelper.FixQuadraticElements(myError);
9229 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9233 //================================================================================
9235 * \brief Makes given elements quadratic
9236 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9237 * \param theElements - elements to make quadratic
9239 //================================================================================
9241 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9242 TIDSortedElemSet& theElements,
9243 const bool theToBiQuad)
9245 if ( theElements.empty() ) return;
9247 // we believe that all theElements are of the same type
9248 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9250 // get all nodes shared by theElements
9251 TIDSortedNodeSet allNodes;
9252 TIDSortedElemSet::iterator eIt = theElements.begin();
9253 for ( ; eIt != theElements.end(); ++eIt )
9254 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9256 // complete theElements with elements of lower dim whose all nodes are in allNodes
9258 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9259 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9260 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9261 for ( ; nIt != allNodes.end(); ++nIt )
9263 const SMDS_MeshNode* n = *nIt;
9264 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9265 while ( invIt->more() )
9267 const SMDS_MeshElement* e = invIt->next();
9268 const SMDSAbs_ElementType type = e->GetType();
9269 if ( e->IsQuadratic() )
9271 quadAdjacentElems[ type ].insert( e );
9274 switch ( e->GetEntityType() ) {
9275 case SMDSEntity_Quad_Triangle:
9276 case SMDSEntity_Quad_Quadrangle:
9277 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9278 case SMDSEntity_BiQuad_Triangle:
9279 case SMDSEntity_BiQuad_Quadrangle:
9280 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9281 default: alreadyOK = true;
9286 if ( type >= elemType )
9287 continue; // same type or more complex linear element
9289 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9290 continue; // e is already checked
9294 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9295 while ( nodeIt->more() && allIn )
9296 allIn = allNodes.count( nodeIt->next() );
9298 theElements.insert(e );
9302 SMESH_MesherHelper helper(*myMesh);
9303 helper.SetIsQuadratic( true );
9304 helper.SetIsBiQuadratic( theToBiQuad );
9306 // add links of quadratic adjacent elements to the helper
9308 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9309 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9310 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9312 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9314 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9315 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9316 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9318 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9320 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9321 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9322 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9324 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9327 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9329 SMESHDS_Mesh* meshDS = GetMeshDS();
9330 SMESHDS_SubMesh* smDS = 0;
9331 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9333 const SMDS_MeshElement* elem = *eIt;
9336 int nbCentralNodes = 0;
9337 switch ( elem->GetEntityType() ) {
9338 // linear convertible
9339 case SMDSEntity_Edge:
9340 case SMDSEntity_Triangle:
9341 case SMDSEntity_Quadrangle:
9342 case SMDSEntity_Tetra:
9343 case SMDSEntity_Pyramid:
9344 case SMDSEntity_Hexa:
9345 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9346 // quadratic that can become bi-quadratic
9347 case SMDSEntity_Quad_Triangle:
9348 case SMDSEntity_Quad_Quadrangle:
9349 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9351 case SMDSEntity_BiQuad_Triangle:
9352 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9353 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9355 default: alreadyOK = true;
9357 if ( alreadyOK ) continue;
9359 const SMDSAbs_ElementType type = elem->GetType();
9360 const int id = elem->GetID();
9361 const int nbNodes = elem->NbCornerNodes();
9362 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9364 helper.SetSubShape( elem->getshapeId() );
9366 if ( !smDS || !smDS->Contains( elem ))
9367 smDS = meshDS->MeshElements( elem->getshapeId() );
9368 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9370 SMDS_MeshElement * newElem = 0;
9373 case 4: // cases for most frequently used element types go first (for optimization)
9374 if ( type == SMDSAbs_Volume )
9375 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9377 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9380 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9381 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9384 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9387 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9390 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9391 nodes[4], id, theForce3d);
9394 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9395 nodes[4], nodes[5], id, theForce3d);
9399 ReplaceElemInGroups( elem, newElem, meshDS);
9400 if( newElem && smDS )
9401 smDS->AddElement( newElem );
9403 // remove central nodes
9404 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9405 if ( nodes[i]->NbInverseElements() == 0 )
9406 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9408 } // loop on theElements
9411 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9412 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9413 // helper.FixQuadraticElements( myError );
9414 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9418 //=======================================================================
9420 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9421 * \return int - nb of checked elements
9423 //=======================================================================
9425 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9426 SMDS_ElemIteratorPtr theItr,
9427 const int theShapeID)
9430 SMESHDS_Mesh* meshDS = GetMeshDS();
9431 ElemFeatures elemType;
9432 vector<const SMDS_MeshNode *> nodes;
9434 while( theItr->more() )
9436 const SMDS_MeshElement* elem = theItr->next();
9438 if( elem && elem->IsQuadratic())
9441 int nbCornerNodes = elem->NbCornerNodes();
9442 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9444 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9446 //remove a quadratic element
9447 if ( !theSm || !theSm->Contains( elem ))
9448 theSm = meshDS->MeshElements( elem->getshapeId() );
9449 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9451 // remove medium nodes
9452 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9453 if ( nodes[i]->NbInverseElements() == 0 )
9454 meshDS->RemoveFreeNode( nodes[i], theSm );
9456 // add a linear element
9457 nodes.resize( nbCornerNodes );
9458 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9459 ReplaceElemInGroups(elem, newElem, meshDS);
9460 if( theSm && newElem )
9461 theSm->AddElement( newElem );
9467 //=======================================================================
9468 //function : ConvertFromQuadratic
9470 //=======================================================================
9472 bool SMESH_MeshEditor::ConvertFromQuadratic()
9474 int nbCheckedElems = 0;
9475 if ( myMesh->HasShapeToMesh() )
9477 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9479 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9480 while ( smIt->more() ) {
9481 SMESH_subMesh* sm = smIt->next();
9482 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9483 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9489 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9490 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9492 SMESHDS_SubMesh *aSM = 0;
9493 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9501 //================================================================================
9503 * \brief Return true if all medium nodes of the element are in the node set
9505 //================================================================================
9507 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9509 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9510 if ( !nodeSet.count( elem->GetNode(i) ))
9516 //================================================================================
9518 * \brief Makes given elements linear
9520 //================================================================================
9522 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9524 if ( theElements.empty() ) return;
9526 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9527 set<int> mediumNodeIDs;
9528 TIDSortedElemSet::iterator eIt = theElements.begin();
9529 for ( ; eIt != theElements.end(); ++eIt )
9531 const SMDS_MeshElement* e = *eIt;
9532 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9533 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9536 // replace given elements by linear ones
9537 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9538 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9540 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9541 // except those elements sharing medium nodes of quadratic element whose medium nodes
9542 // are not all in mediumNodeIDs
9544 // get remaining medium nodes
9545 TIDSortedNodeSet mediumNodes;
9546 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9547 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9548 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9549 mediumNodes.insert( mediumNodes.end(), n );
9551 // find more quadratic elements to convert
9552 TIDSortedElemSet moreElemsToConvert;
9553 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9554 for ( ; nIt != mediumNodes.end(); ++nIt )
9556 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9557 while ( invIt->more() )
9559 const SMDS_MeshElement* e = invIt->next();
9560 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9562 // find a more complex element including e and
9563 // whose medium nodes are not in mediumNodes
9564 bool complexFound = false;
9565 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9567 SMDS_ElemIteratorPtr invIt2 =
9568 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9569 while ( invIt2->more() )
9571 const SMDS_MeshElement* eComplex = invIt2->next();
9572 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9574 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9575 if ( nbCommonNodes == e->NbNodes())
9577 complexFound = true;
9578 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9584 if ( !complexFound )
9585 moreElemsToConvert.insert( e );
9589 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9590 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9593 //=======================================================================
9594 //function : SewSideElements
9596 //=======================================================================
9598 SMESH_MeshEditor::Sew_Error
9599 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9600 TIDSortedElemSet& theSide2,
9601 const SMDS_MeshNode* theFirstNode1,
9602 const SMDS_MeshNode* theFirstNode2,
9603 const SMDS_MeshNode* theSecondNode1,
9604 const SMDS_MeshNode* theSecondNode2)
9608 if ( theSide1.size() != theSide2.size() )
9609 return SEW_DIFF_NB_OF_ELEMENTS;
9611 Sew_Error aResult = SEW_OK;
9613 // 1. Build set of faces representing each side
9614 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9615 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9617 // =======================================================================
9618 // 1. Build set of faces representing each side:
9619 // =======================================================================
9620 // a. build set of nodes belonging to faces
9621 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9622 // c. create temporary faces representing side of volumes if correspondent
9623 // face does not exist
9625 SMESHDS_Mesh* aMesh = GetMeshDS();
9626 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9627 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9628 TIDSortedElemSet faceSet1, faceSet2;
9629 set<const SMDS_MeshElement*> volSet1, volSet2;
9630 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9631 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9632 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9633 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9634 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9635 int iSide, iFace, iNode;
9637 list<const SMDS_MeshElement* > tempFaceList;
9638 for ( iSide = 0; iSide < 2; iSide++ ) {
9639 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9640 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9641 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9642 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9643 set<const SMDS_MeshElement*>::iterator vIt;
9644 TIDSortedElemSet::iterator eIt;
9645 set<const SMDS_MeshNode*>::iterator nIt;
9647 // check that given nodes belong to given elements
9648 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9649 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9650 int firstIndex = -1, secondIndex = -1;
9651 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9652 const SMDS_MeshElement* elem = *eIt;
9653 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9654 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9655 if ( firstIndex > -1 && secondIndex > -1 ) break;
9657 if ( firstIndex < 0 || secondIndex < 0 ) {
9658 // we can simply return until temporary faces created
9659 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9662 // -----------------------------------------------------------
9663 // 1a. Collect nodes of existing faces
9664 // and build set of face nodes in order to detect missing
9665 // faces corresponding to sides of volumes
9666 // -----------------------------------------------------------
9668 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9670 // loop on the given element of a side
9671 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9672 //const SMDS_MeshElement* elem = *eIt;
9673 const SMDS_MeshElement* elem = *eIt;
9674 if ( elem->GetType() == SMDSAbs_Face ) {
9675 faceSet->insert( elem );
9676 set <const SMDS_MeshNode*> faceNodeSet;
9677 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9678 while ( nodeIt->more() ) {
9679 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9680 nodeSet->insert( n );
9681 faceNodeSet.insert( n );
9683 setOfFaceNodeSet.insert( faceNodeSet );
9685 else if ( elem->GetType() == SMDSAbs_Volume )
9686 volSet->insert( elem );
9688 // ------------------------------------------------------------------------------
9689 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9690 // ------------------------------------------------------------------------------
9692 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9693 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9694 while ( fIt->more() ) { // loop on faces sharing a node
9695 const SMDS_MeshElement* f = fIt->next();
9696 if ( faceSet->find( f ) == faceSet->end() ) {
9697 // check if all nodes are in nodeSet and
9698 // complete setOfFaceNodeSet if they are
9699 set <const SMDS_MeshNode*> faceNodeSet;
9700 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9701 bool allInSet = true;
9702 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9703 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9704 if ( nodeSet->find( n ) == nodeSet->end() )
9707 faceNodeSet.insert( n );
9710 faceSet->insert( f );
9711 setOfFaceNodeSet.insert( faceNodeSet );
9717 // -------------------------------------------------------------------------
9718 // 1c. Create temporary faces representing sides of volumes if correspondent
9719 // face does not exist
9720 // -------------------------------------------------------------------------
9722 if ( !volSet->empty() ) {
9723 //int nodeSetSize = nodeSet->size();
9725 // loop on given volumes
9726 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9727 SMDS_VolumeTool vol (*vIt);
9728 // loop on volume faces: find free faces
9729 // --------------------------------------
9730 list<const SMDS_MeshElement* > freeFaceList;
9731 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9732 if ( !vol.IsFreeFace( iFace ))
9734 // check if there is already a face with same nodes in a face set
9735 const SMDS_MeshElement* aFreeFace = 0;
9736 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9737 int nbNodes = vol.NbFaceNodes( iFace );
9738 set <const SMDS_MeshNode*> faceNodeSet;
9739 vol.GetFaceNodes( iFace, faceNodeSet );
9740 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9742 // no such a face is given but it still can exist, check it
9743 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9744 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9747 // create a temporary face
9748 if ( nbNodes == 3 ) {
9749 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9750 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9752 else if ( nbNodes == 4 ) {
9753 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9754 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9757 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9758 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9759 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9762 tempFaceList.push_back( aFreeFace );
9766 freeFaceList.push_back( aFreeFace );
9768 } // loop on faces of a volume
9770 // choose one of several free faces of a volume
9771 // --------------------------------------------
9772 if ( freeFaceList.size() > 1 ) {
9773 // choose a face having max nb of nodes shared by other elems of a side
9774 int maxNbNodes = -1;
9775 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9776 while ( fIt != freeFaceList.end() ) { // loop on free faces
9777 int nbSharedNodes = 0;
9778 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9779 while ( nodeIt->more() ) { // loop on free face nodes
9780 const SMDS_MeshNode* n =
9781 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9782 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9783 while ( invElemIt->more() ) {
9784 const SMDS_MeshElement* e = invElemIt->next();
9785 nbSharedNodes += faceSet->count( e );
9786 nbSharedNodes += elemSet->count( e );
9789 if ( nbSharedNodes > maxNbNodes ) {
9790 maxNbNodes = nbSharedNodes;
9791 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9793 else if ( nbSharedNodes == maxNbNodes ) {
9797 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9800 if ( freeFaceList.size() > 1 )
9802 // could not choose one face, use another way
9803 // choose a face most close to the bary center of the opposite side
9804 gp_XYZ aBC( 0., 0., 0. );
9805 set <const SMDS_MeshNode*> addedNodes;
9806 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9807 eIt = elemSet2->begin();
9808 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9809 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9810 while ( nodeIt->more() ) { // loop on free face nodes
9811 const SMDS_MeshNode* n =
9812 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9813 if ( addedNodes.insert( n ).second )
9814 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9817 aBC /= addedNodes.size();
9818 double minDist = DBL_MAX;
9819 fIt = freeFaceList.begin();
9820 while ( fIt != freeFaceList.end() ) { // loop on free faces
9822 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9823 while ( nodeIt->more() ) { // loop on free face nodes
9824 const SMDS_MeshNode* n =
9825 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9826 gp_XYZ p( n->X(),n->Y(),n->Z() );
9827 dist += ( aBC - p ).SquareModulus();
9829 if ( dist < minDist ) {
9831 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9834 fIt = freeFaceList.erase( fIt++ );
9837 } // choose one of several free faces of a volume
9839 if ( freeFaceList.size() == 1 ) {
9840 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9841 faceSet->insert( aFreeFace );
9842 // complete a node set with nodes of a found free face
9843 // for ( iNode = 0; iNode < ; iNode++ )
9844 // nodeSet->insert( fNodes[ iNode ] );
9847 } // loop on volumes of a side
9849 // // complete a set of faces if new nodes in a nodeSet appeared
9850 // // ----------------------------------------------------------
9851 // if ( nodeSetSize != nodeSet->size() ) {
9852 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9853 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9854 // while ( fIt->more() ) { // loop on faces sharing a node
9855 // const SMDS_MeshElement* f = fIt->next();
9856 // if ( faceSet->find( f ) == faceSet->end() ) {
9857 // // check if all nodes are in nodeSet and
9858 // // complete setOfFaceNodeSet if they are
9859 // set <const SMDS_MeshNode*> faceNodeSet;
9860 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9861 // bool allInSet = true;
9862 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9863 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9864 // if ( nodeSet->find( n ) == nodeSet->end() )
9865 // allInSet = false;
9867 // faceNodeSet.insert( n );
9869 // if ( allInSet ) {
9870 // faceSet->insert( f );
9871 // setOfFaceNodeSet.insert( faceNodeSet );
9877 } // Create temporary faces, if there are volumes given
9880 if ( faceSet1.size() != faceSet2.size() ) {
9881 // delete temporary faces: they are in reverseElements of actual nodes
9882 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9883 // while ( tmpFaceIt->more() )
9884 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9885 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9886 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9887 // aMesh->RemoveElement(*tmpFaceIt);
9888 MESSAGE("Diff nb of faces");
9889 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9892 // ============================================================
9893 // 2. Find nodes to merge:
9894 // bind a node to remove to a node to put instead
9895 // ============================================================
9897 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9898 if ( theFirstNode1 != theFirstNode2 )
9899 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9900 if ( theSecondNode1 != theSecondNode2 )
9901 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9903 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9904 set< long > linkIdSet; // links to process
9905 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9907 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9908 list< NLink > linkList[2];
9909 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9910 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9911 // loop on links in linkList; find faces by links and append links
9912 // of the found faces to linkList
9913 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9914 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9916 NLink link[] = { *linkIt[0], *linkIt[1] };
9917 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9918 if ( !linkIdSet.count( linkID ) )
9921 // by links, find faces in the face sets,
9922 // and find indices of link nodes in the found faces;
9923 // in a face set, there is only one or no face sharing a link
9924 // ---------------------------------------------------------------
9926 const SMDS_MeshElement* face[] = { 0, 0 };
9927 vector<const SMDS_MeshNode*> fnodes[2];
9928 int iLinkNode[2][2];
9929 TIDSortedElemSet avoidSet;
9930 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9931 const SMDS_MeshNode* n1 = link[iSide].first;
9932 const SMDS_MeshNode* n2 = link[iSide].second;
9933 //cout << "Side " << iSide << " ";
9934 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9935 // find a face by two link nodes
9936 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9937 *faceSetPtr[ iSide ], avoidSet,
9938 &iLinkNode[iSide][0],
9939 &iLinkNode[iSide][1] );
9942 //cout << " F " << face[ iSide]->GetID() <<endl;
9943 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9944 // put face nodes to fnodes
9945 if ( face[ iSide ]->IsQuadratic() )
9947 // use interlaced nodes iterator
9948 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9949 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9950 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9951 while ( nIter->more() )
9952 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9956 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9957 face[ iSide ]->end_nodes() );
9959 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9963 // check similarity of elements of the sides
9964 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9965 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9966 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9967 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9970 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9972 break; // do not return because it's necessary to remove tmp faces
9975 // set nodes to merge
9976 // -------------------
9978 if ( face[0] && face[1] ) {
9979 const int nbNodes = face[0]->NbNodes();
9980 if ( nbNodes != face[1]->NbNodes() ) {
9981 MESSAGE("Diff nb of face nodes");
9982 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9983 break; // do not return because it s necessary to remove tmp faces
9985 bool reverse[] = { false, false }; // order of nodes in the link
9986 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9987 // analyse link orientation in faces
9988 int i1 = iLinkNode[ iSide ][ 0 ];
9989 int i2 = iLinkNode[ iSide ][ 1 ];
9990 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9992 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9993 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9994 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9996 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9997 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10000 // add other links of the faces to linkList
10001 // -----------------------------------------
10003 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10004 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10005 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10006 if ( !iter_isnew.second ) { // already in a set: no need to process
10007 linkIdSet.erase( iter_isnew.first );
10009 else // new in set == encountered for the first time: add
10011 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10012 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10013 linkList[0].push_back ( NLink( n1, n2 ));
10014 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10019 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10022 } // loop on link lists
10024 if ( aResult == SEW_OK &&
10025 ( //linkIt[0] != linkList[0].end() ||
10026 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10027 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10028 " " << (faceSetPtr[1]->empty()));
10029 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10032 // ====================================================================
10033 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10034 // ====================================================================
10036 // delete temporary faces
10037 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10038 // while ( tmpFaceIt->more() )
10039 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10040 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10041 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10042 aMesh->RemoveElement(*tmpFaceIt);
10044 if ( aResult != SEW_OK)
10047 list< int > nodeIDsToRemove;
10048 vector< const SMDS_MeshNode*> nodes;
10049 ElemFeatures elemType;
10051 // loop on nodes replacement map
10052 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10053 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10054 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10056 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10057 nodeIDsToRemove.push_back( nToRemove->GetID() );
10058 // loop on elements sharing nToRemove
10059 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10060 while ( invElemIt->more() ) {
10061 const SMDS_MeshElement* e = invElemIt->next();
10062 // get a new suite of nodes: make replacement
10063 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10064 nodes.resize( nbNodes );
10065 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10066 while ( nIt->more() ) {
10067 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10068 nnIt = nReplaceMap.find( n );
10069 if ( nnIt != nReplaceMap.end() ) {
10071 n = (*nnIt).second;
10075 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10076 // elemIDsToRemove.push_back( e->GetID() );
10080 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10081 aMesh->RemoveElement( e );
10083 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10085 AddToSameGroups( newElem, e, aMesh );
10086 if ( int aShapeId = e->getshapeId() )
10087 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10093 Remove( nodeIDsToRemove, true );
10098 //================================================================================
10100 * \brief Find corresponding nodes in two sets of faces
10101 * \param theSide1 - first face set
10102 * \param theSide2 - second first face
10103 * \param theFirstNode1 - a boundary node of set 1
10104 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10105 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10106 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10107 * \param nReplaceMap - output map of corresponding nodes
10108 * \return bool - is a success or not
10110 //================================================================================
10113 //#define DEBUG_MATCHING_NODES
10116 SMESH_MeshEditor::Sew_Error
10117 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10118 set<const SMDS_MeshElement*>& theSide2,
10119 const SMDS_MeshNode* theFirstNode1,
10120 const SMDS_MeshNode* theFirstNode2,
10121 const SMDS_MeshNode* theSecondNode1,
10122 const SMDS_MeshNode* theSecondNode2,
10123 TNodeNodeMap & nReplaceMap)
10125 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10127 nReplaceMap.clear();
10128 if ( theFirstNode1 != theFirstNode2 )
10129 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10130 if ( theSecondNode1 != theSecondNode2 )
10131 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10133 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10134 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10136 list< NLink > linkList[2];
10137 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10138 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10140 // loop on links in linkList; find faces by links and append links
10141 // of the found faces to linkList
10142 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10143 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10144 NLink link[] = { *linkIt[0], *linkIt[1] };
10145 if ( linkSet.find( link[0] ) == linkSet.end() )
10148 // by links, find faces in the face sets,
10149 // and find indices of link nodes in the found faces;
10150 // in a face set, there is only one or no face sharing a link
10151 // ---------------------------------------------------------------
10153 const SMDS_MeshElement* face[] = { 0, 0 };
10154 list<const SMDS_MeshNode*> notLinkNodes[2];
10155 //bool reverse[] = { false, false }; // order of notLinkNodes
10157 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10159 const SMDS_MeshNode* n1 = link[iSide].first;
10160 const SMDS_MeshNode* n2 = link[iSide].second;
10161 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10162 set< const SMDS_MeshElement* > facesOfNode1;
10163 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10165 // during a loop of the first node, we find all faces around n1,
10166 // during a loop of the second node, we find one face sharing both n1 and n2
10167 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10168 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10169 while ( fIt->more() ) { // loop on faces sharing a node
10170 const SMDS_MeshElement* f = fIt->next();
10171 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10172 ! facesOfNode1.insert( f ).second ) // f encounters twice
10174 if ( face[ iSide ] ) {
10175 MESSAGE( "2 faces per link " );
10176 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10179 faceSet->erase( f );
10181 // get not link nodes
10182 int nbN = f->NbNodes();
10183 if ( f->IsQuadratic() )
10185 nbNodes[ iSide ] = nbN;
10186 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10187 int i1 = f->GetNodeIndex( n1 );
10188 int i2 = f->GetNodeIndex( n2 );
10189 int iEnd = nbN, iBeg = -1, iDelta = 1;
10190 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10192 std::swap( iEnd, iBeg ); iDelta = -1;
10197 if ( i == iEnd ) i = iBeg + iDelta;
10198 if ( i == i1 ) break;
10199 nodes.push_back ( f->GetNode( i ) );
10205 // check similarity of elements of the sides
10206 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10207 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10208 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10209 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10212 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10216 // set nodes to merge
10217 // -------------------
10219 if ( face[0] && face[1] ) {
10220 if ( nbNodes[0] != nbNodes[1] ) {
10221 MESSAGE("Diff nb of face nodes");
10222 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10224 #ifdef DEBUG_MATCHING_NODES
10225 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10226 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10227 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10229 int nbN = nbNodes[0];
10231 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10232 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10233 for ( int i = 0 ; i < nbN - 2; ++i ) {
10234 #ifdef DEBUG_MATCHING_NODES
10235 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10237 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10241 // add other links of the face 1 to linkList
10242 // -----------------------------------------
10244 const SMDS_MeshElement* f0 = face[0];
10245 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10246 for ( int i = 0; i < nbN; i++ )
10248 const SMDS_MeshNode* n2 = f0->GetNode( i );
10249 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10250 linkSet.insert( SMESH_TLink( n1, n2 ));
10251 if ( !iter_isnew.second ) { // already in a set: no need to process
10252 linkSet.erase( iter_isnew.first );
10254 else // new in set == encountered for the first time: add
10256 #ifdef DEBUG_MATCHING_NODES
10257 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10258 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10260 linkList[0].push_back ( NLink( n1, n2 ));
10261 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10266 } // loop on link lists
10271 //================================================================================
10273 * \brief Create elements equal (on same nodes) to given ones
10274 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10275 * elements of the uppest dimension are duplicated.
10277 //================================================================================
10279 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10281 ClearLastCreated();
10282 SMESHDS_Mesh* mesh = GetMeshDS();
10284 // get an element type and an iterator over elements
10286 SMDSAbs_ElementType type = SMDSAbs_All;
10287 SMDS_ElemIteratorPtr elemIt;
10288 vector< const SMDS_MeshElement* > allElems;
10289 if ( theElements.empty() )
10291 if ( mesh->NbNodes() == 0 )
10293 // get most complex type
10294 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10295 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10296 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10298 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10299 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10304 elemIt = mesh->elementsIterator( type );
10308 type = (*theElements.begin())->GetType();
10309 elemIt = SMESHUtils::elemSetIterator( theElements );
10312 // duplicate elements
10314 ElemFeatures elemType;
10316 vector< const SMDS_MeshNode* > nodes;
10317 while ( elemIt->more() )
10319 const SMDS_MeshElement* elem = elemIt->next();
10320 if ( elem->GetType() != type )
10323 elemType.Init( elem, /*basicOnly=*/false );
10324 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10326 AddElement( nodes, elemType );
10330 //================================================================================
10332 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10333 \param theElems - the list of elements (edges or faces) to be replicated
10334 The nodes for duplication could be found from these elements
10335 \param theNodesNot - list of nodes to NOT replicate
10336 \param theAffectedElems - the list of elements (cells and edges) to which the
10337 replicated nodes should be associated to.
10338 \return TRUE if operation has been completed successfully, FALSE otherwise
10340 //================================================================================
10342 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10343 const TIDSortedElemSet& theNodesNot,
10344 const TIDSortedElemSet& theAffectedElems )
10346 ClearLastCreated();
10348 if ( theElems.size() == 0 )
10351 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10356 TNodeNodeMap anOldNodeToNewNode;
10357 // duplicate elements and nodes
10358 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10359 // replce nodes by duplications
10360 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10364 //================================================================================
10366 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10367 \param theMeshDS - mesh instance
10368 \param theElems - the elements replicated or modified (nodes should be changed)
10369 \param theNodesNot - nodes to NOT replicate
10370 \param theNodeNodeMap - relation of old node to new created node
10371 \param theIsDoubleElem - flag os to replicate element or modify
10372 \return TRUE if operation has been completed successfully, FALSE otherwise
10374 //================================================================================
10376 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10377 const TIDSortedElemSet& theElems,
10378 const TIDSortedElemSet& theNodesNot,
10379 TNodeNodeMap& theNodeNodeMap,
10380 const bool theIsDoubleElem )
10382 // iterate through element and duplicate them (by nodes duplication)
10384 std::vector<const SMDS_MeshNode*> newNodes;
10385 ElemFeatures elemType;
10387 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10388 for ( ; elemItr != theElems.end(); ++elemItr )
10390 const SMDS_MeshElement* anElem = *elemItr;
10394 // duplicate nodes to duplicate element
10395 bool isDuplicate = false;
10396 newNodes.resize( anElem->NbNodes() );
10397 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10399 while ( anIter->more() )
10401 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10402 const SMDS_MeshNode* aNewNode = aCurrNode;
10403 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10404 if ( n2n != theNodeNodeMap.end() )
10406 aNewNode = n2n->second;
10408 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10411 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10412 copyPosition( aCurrNode, aNewNode );
10413 theNodeNodeMap[ aCurrNode ] = aNewNode;
10414 myLastCreatedNodes.push_back( aNewNode );
10416 isDuplicate |= (aCurrNode != aNewNode);
10417 newNodes[ ind++ ] = aNewNode;
10419 if ( !isDuplicate )
10422 if ( theIsDoubleElem )
10423 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10425 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10432 //================================================================================
10434 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10435 \param theNodes - identifiers of nodes to be doubled
10436 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10437 nodes. If list of element identifiers is empty then nodes are doubled but
10438 they not assigned to elements
10439 \return TRUE if operation has been completed successfully, FALSE otherwise
10441 //================================================================================
10443 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10444 const std::list< int >& theListOfModifiedElems )
10446 ClearLastCreated();
10448 if ( theListOfNodes.size() == 0 )
10451 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10455 // iterate through nodes and duplicate them
10457 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10459 std::list< int >::const_iterator aNodeIter;
10460 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10462 int aCurr = *aNodeIter;
10463 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10469 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10472 copyPosition( aNode, aNewNode );
10473 anOldNodeToNewNode[ aNode ] = aNewNode;
10474 myLastCreatedNodes.push_back( aNewNode );
10478 // Create map of new nodes for modified elements
10480 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10482 std::list< int >::const_iterator anElemIter;
10483 for ( anElemIter = theListOfModifiedElems.begin();
10484 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10486 int aCurr = *anElemIter;
10487 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10491 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10493 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10495 while ( anIter->more() )
10497 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10498 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10500 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10501 aNodeArr[ ind++ ] = aNewNode;
10504 aNodeArr[ ind++ ] = aCurrNode;
10506 anElemToNodes[ anElem ] = aNodeArr;
10509 // Change nodes of elements
10511 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10512 anElemToNodesIter = anElemToNodes.begin();
10513 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10515 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10516 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10519 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10528 //================================================================================
10530 \brief Check if element located inside shape
10531 \return TRUE if IN or ON shape, FALSE otherwise
10533 //================================================================================
10535 template<class Classifier>
10536 bool isInside(const SMDS_MeshElement* theElem,
10537 Classifier& theClassifier,
10538 const double theTol)
10540 gp_XYZ centerXYZ (0, 0, 0);
10541 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10542 while (aNodeItr->more())
10543 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10545 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10546 theClassifier.Perform(aPnt, theTol);
10547 TopAbs_State aState = theClassifier.State();
10548 return (aState == TopAbs_IN || aState == TopAbs_ON );
10551 //================================================================================
10553 * \brief Classifier of the 3D point on the TopoDS_Face
10554 * with interaface suitable for isInside()
10556 //================================================================================
10558 struct _FaceClassifier
10560 Extrema_ExtPS _extremum;
10561 BRepAdaptor_Surface _surface;
10562 TopAbs_State _state;
10564 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10566 _extremum.Initialize( _surface,
10567 _surface.FirstUParameter(), _surface.LastUParameter(),
10568 _surface.FirstVParameter(), _surface.LastVParameter(),
10569 _surface.Tolerance(), _surface.Tolerance() );
10571 void Perform(const gp_Pnt& aPnt, double theTol)
10574 _state = TopAbs_OUT;
10575 _extremum.Perform(aPnt);
10576 if ( _extremum.IsDone() )
10577 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10578 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10580 TopAbs_State State() const
10587 //================================================================================
10589 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10590 This method is the first step of DoubleNodeElemGroupsInRegion.
10591 \param theElems - list of groups of elements (edges or faces) to be replicated
10592 \param theNodesNot - list of groups of nodes not to replicated
10593 \param theShape - shape to detect affected elements (element which geometric center
10594 located on or inside shape). If the shape is null, detection is done on faces orientations
10595 (select elements with a gravity center on the side given by faces normals).
10596 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10597 The replicated nodes should be associated to affected elements.
10598 \return groups of affected elements
10599 \sa DoubleNodeElemGroupsInRegion()
10601 //================================================================================
10603 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10604 const TIDSortedElemSet& theNodesNot,
10605 const TopoDS_Shape& theShape,
10606 TIDSortedElemSet& theAffectedElems)
10608 if ( theShape.IsNull() )
10610 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10611 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10612 std::set<const SMDS_MeshElement*> edgesToCheck;
10613 alreadyCheckedNodes.clear();
10614 alreadyCheckedElems.clear();
10615 edgesToCheck.clear();
10617 // --- iterates on elements to be replicated and get elements by back references from their nodes
10619 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10620 for ( ; elemItr != theElems.end(); ++elemItr )
10622 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10623 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10626 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10627 std::set<const SMDS_MeshNode*> nodesElem;
10629 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10630 while ( nodeItr->more() )
10632 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10633 nodesElem.insert(aNode);
10635 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10636 for (; nodit != nodesElem.end(); nodit++)
10638 const SMDS_MeshNode* aNode = *nodit;
10639 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10641 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10643 alreadyCheckedNodes.insert(aNode);
10644 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10645 while ( backElemItr->more() )
10647 const SMDS_MeshElement* curElem = backElemItr->next();
10648 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10650 if (theElems.find(curElem) != theElems.end())
10652 alreadyCheckedElems.insert(curElem);
10653 double x=0, y=0, z=0;
10655 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10656 while ( nodeItr2->more() )
10658 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10659 x += anotherNode->X();
10660 y += anotherNode->Y();
10661 z += anotherNode->Z();
10665 p.SetCoord( x/nb -aNode->X(),
10667 z/nb -aNode->Z() );
10670 theAffectedElems.insert( curElem );
10672 else if (curElem->GetType() == SMDSAbs_Edge)
10673 edgesToCheck.insert(curElem);
10677 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10678 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10679 for( ; eit != edgesToCheck.end(); eit++)
10681 bool onside = true;
10682 const SMDS_MeshElement* anEdge = *eit;
10683 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10684 while ( nodeItr->more() )
10686 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10687 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10695 theAffectedElems.insert(anEdge);
10701 const double aTol = Precision::Confusion();
10702 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10703 auto_ptr<_FaceClassifier> aFaceClassifier;
10704 if ( theShape.ShapeType() == TopAbs_SOLID )
10706 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10707 bsc3d->PerformInfinitePoint(aTol);
10709 else if (theShape.ShapeType() == TopAbs_FACE )
10711 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10714 // iterates on indicated elements and get elements by back references from their nodes
10715 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10716 for ( ; elemItr != theElems.end(); ++elemItr )
10718 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10721 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722 while ( nodeItr->more() )
10724 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10727 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10728 while ( backElemItr->more() )
10730 const SMDS_MeshElement* curElem = backElemItr->next();
10731 if ( curElem && theElems.find(curElem) == theElems.end() &&
10733 isInside( curElem, *bsc3d, aTol ) :
10734 isInside( curElem, *aFaceClassifier, aTol )))
10735 theAffectedElems.insert( curElem );
10743 //================================================================================
10745 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10746 \param theElems - group of of elements (edges or faces) to be replicated
10747 \param theNodesNot - group of nodes not to replicate
10748 \param theShape - shape to detect affected elements (element which geometric center
10749 located on or inside shape).
10750 The replicated nodes should be associated to affected elements.
10751 \return TRUE if operation has been completed successfully, FALSE otherwise
10753 //================================================================================
10755 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10756 const TIDSortedElemSet& theNodesNot,
10757 const TopoDS_Shape& theShape )
10759 if ( theShape.IsNull() )
10762 const double aTol = Precision::Confusion();
10763 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10764 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10765 if ( theShape.ShapeType() == TopAbs_SOLID )
10767 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10768 bsc3d->PerformInfinitePoint(aTol);
10770 else if (theShape.ShapeType() == TopAbs_FACE )
10772 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10775 // iterates on indicated elements and get elements by back references from their nodes
10776 TIDSortedElemSet anAffected;
10777 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10778 for ( ; elemItr != theElems.end(); ++elemItr )
10780 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10784 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10785 while ( nodeItr->more() )
10787 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10788 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10790 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10791 while ( backElemItr->more() )
10793 const SMDS_MeshElement* curElem = backElemItr->next();
10794 if ( curElem && theElems.find(curElem) == theElems.end() &&
10796 isInside( curElem, *bsc3d, aTol ) :
10797 isInside( curElem, *aFaceClassifier, aTol )))
10798 anAffected.insert( curElem );
10802 return DoubleNodes( theElems, theNodesNot, anAffected );
10806 * \brief compute an oriented angle between two planes defined by four points.
10807 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10808 * @param p0 base of the rotation axe
10809 * @param p1 extremity of the rotation axe
10810 * @param g1 belongs to the first plane
10811 * @param g2 belongs to the second plane
10813 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10815 gp_Vec vref(p0, p1);
10818 gp_Vec n1 = vref.Crossed(v1);
10819 gp_Vec n2 = vref.Crossed(v2);
10821 return n2.AngleWithRef(n1, vref);
10823 catch ( Standard_Failure ) {
10825 return Max( v1.Magnitude(), v2.Magnitude() );
10829 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10830 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10831 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10832 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10833 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10834 * 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.
10835 * 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.
10836 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10837 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10838 * \param theElems - list of groups of volumes, where a group of volume is a set of
10839 * SMDS_MeshElements sorted by Id.
10840 * \param createJointElems - if TRUE, create the elements
10841 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10842 * the boundary between \a theDomains and the rest mesh
10843 * \return TRUE if operation has been completed successfully, FALSE otherwise
10845 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10846 bool createJointElems,
10847 bool onAllBoundaries)
10849 MESSAGE("----------------------------------------------");
10850 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10851 MESSAGE("----------------------------------------------");
10853 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10854 meshDS->BuildDownWardConnectivity(true);
10856 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10858 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10859 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10860 // build the list of nodes shared by 2 or more domains, with their domain indexes
10862 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10863 std::map<int,int>celldom; // cell vtkId --> domain
10864 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10865 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10866 faceDomains.clear();
10868 cellDomains.clear();
10869 nodeDomains.clear();
10870 std::map<int,int> emptyMap;
10871 std::set<int> emptySet;
10874 MESSAGE(".. Number of domains :"<<theElems.size());
10876 TIDSortedElemSet theRestDomElems;
10877 const int iRestDom = -1;
10878 const int idom0 = onAllBoundaries ? iRestDom : 0;
10879 const int nbDomains = theElems.size();
10881 // Check if the domains do not share an element
10882 for (int idom = 0; idom < nbDomains-1; idom++)
10884 // MESSAGE("... Check of domain #" << idom);
10885 const TIDSortedElemSet& domain = theElems[idom];
10886 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10887 for (; elemItr != domain.end(); ++elemItr)
10889 const SMDS_MeshElement* anElem = *elemItr;
10890 int idombisdeb = idom + 1 ;
10891 // check if the element belongs to a domain further in the list
10892 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
10894 const TIDSortedElemSet& domainbis = theElems[idombis];
10895 if ( domainbis.count( anElem ))
10897 MESSAGE(".... Domain #" << idom);
10898 MESSAGE(".... Domain #" << idombis);
10899 throw SALOME_Exception("The domains are not disjoint.");
10906 for (int idom = 0; idom < nbDomains; idom++)
10909 // --- build a map (face to duplicate --> volume to modify)
10910 // with all the faces shared by 2 domains (group of elements)
10911 // and corresponding volume of this domain, for each shared face.
10912 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10914 MESSAGE("... Neighbors of domain #" << idom);
10915 const TIDSortedElemSet& domain = theElems[idom];
10916 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10917 for (; elemItr != domain.end(); ++elemItr)
10919 const SMDS_MeshElement* anElem = *elemItr;
10922 int vtkId = anElem->getVtkId();
10923 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10924 int neighborsVtkIds[NBMAXNEIGHBORS];
10925 int downIds[NBMAXNEIGHBORS];
10926 unsigned char downTypes[NBMAXNEIGHBORS];
10927 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10928 for (int n = 0; n < nbNeighbors; n++)
10930 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10931 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10932 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10935 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10937 // MESSAGE("Domain " << idombis);
10938 const TIDSortedElemSet& domainbis = theElems[idombis];
10939 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10941 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10943 DownIdType face(downIds[n], downTypes[n]);
10944 if (!faceDomains[face].count(idom))
10946 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10947 celldom[vtkId] = idom;
10948 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10952 theRestDomElems.insert( elem );
10953 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10954 celldom[neighborsVtkIds[n]] = iRestDom;
10962 //MESSAGE("Number of shared faces " << faceDomains.size());
10963 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10965 // --- explore the shared faces domain by domain,
10966 // explore the nodes of the face and see if they belong to a cell in the domain,
10967 // which has only a node or an edge on the border (not a shared face)
10969 for (int idomain = idom0; idomain < nbDomains; idomain++)
10971 //MESSAGE("Domain " << idomain);
10972 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10973 itface = faceDomains.begin();
10974 for (; itface != faceDomains.end(); ++itface)
10976 const std::map<int, int>& domvol = itface->second;
10977 if (!domvol.count(idomain))
10979 DownIdType face = itface->first;
10980 //MESSAGE(" --- face " << face.cellId);
10981 std::set<int> oldNodes;
10983 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10984 std::set<int>::iterator itn = oldNodes.begin();
10985 for (; itn != oldNodes.end(); ++itn)
10988 //MESSAGE(" node " << oldId);
10989 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10990 for (int i=0; i<l.ncells; i++)
10992 int vtkId = l.cells[i];
10993 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10994 if (!domain.count(anElem))
10996 int vtkType = grid->GetCellType(vtkId);
10997 int downId = grid->CellIdToDownId(vtkId);
11000 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11001 continue; // not OK at this stage of the algorithm:
11002 //no cells created after BuildDownWardConnectivity
11004 DownIdType aCell(downId, vtkType);
11005 cellDomains[aCell][idomain] = vtkId;
11006 celldom[vtkId] = idomain;
11007 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11013 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11014 // for each shared face, get the nodes
11015 // for each node, for each domain of the face, create a clone of the node
11017 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11018 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11019 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11021 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11022 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11023 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11025 MESSAGE(".. Duplication of the nodes");
11026 for (int idomain = idom0; idomain < nbDomains; idomain++)
11028 itface = faceDomains.begin();
11029 for (; itface != faceDomains.end(); ++itface)
11031 const std::map<int, int>& domvol = itface->second;
11032 if (!domvol.count(idomain))
11034 DownIdType face = itface->first;
11035 //MESSAGE(" --- face " << face.cellId);
11036 std::set<int> oldNodes;
11038 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11039 std::set<int>::iterator itn = oldNodes.begin();
11040 for (; itn != oldNodes.end(); ++itn)
11043 if (nodeDomains[oldId].empty())
11045 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11046 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11048 std::map<int, int>::const_iterator itdom = domvol.begin();
11049 for (; itdom != domvol.end(); ++itdom)
11051 int idom = itdom->first;
11052 //MESSAGE(" domain " << idom);
11053 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11055 if (nodeDomains[oldId].size() >= 2) // a multiple node
11057 vector<int> orderedDoms;
11058 //MESSAGE("multiple node " << oldId);
11059 if (mutipleNodes.count(oldId))
11060 orderedDoms = mutipleNodes[oldId];
11063 map<int,int>::iterator it = nodeDomains[oldId].begin();
11064 for (; it != nodeDomains[oldId].end(); ++it)
11065 orderedDoms.push_back(it->first);
11067 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11068 //stringstream txt;
11069 //for (int i=0; i<orderedDoms.size(); i++)
11070 // txt << orderedDoms[i] << " ";
11071 //MESSAGE("orderedDoms " << txt.str());
11072 mutipleNodes[oldId] = orderedDoms;
11074 double *coords = grid->GetPoint(oldId);
11075 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11076 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11077 int newId = newNode->getVtkId();
11078 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11079 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11086 MESSAGE(".. Creation of elements");
11087 for (int idomain = idom0; idomain < nbDomains; idomain++)
11089 itface = faceDomains.begin();
11090 for (; itface != faceDomains.end(); ++itface)
11092 std::map<int, int> domvol = itface->second;
11093 if (!domvol.count(idomain))
11095 DownIdType face = itface->first;
11096 //MESSAGE(" --- face " << face.cellId);
11097 std::set<int> oldNodes;
11099 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11100 int nbMultipleNodes = 0;
11101 std::set<int>::iterator itn = oldNodes.begin();
11102 for (; itn != oldNodes.end(); ++itn)
11105 if (mutipleNodes.count(oldId))
11108 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11110 //MESSAGE("multiple Nodes detected on a shared face");
11111 int downId = itface->first.cellId;
11112 unsigned char cellType = itface->first.cellType;
11113 // --- shared edge or shared face ?
11114 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11117 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11118 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11119 if (mutipleNodes.count(nodes[i]))
11120 if (!mutipleNodesToFace.count(nodes[i]))
11121 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11123 else // shared face (between two volumes)
11125 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11126 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11127 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11128 for (int ie =0; ie < nbEdges; ie++)
11131 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11132 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11134 vector<int> vn0 = mutipleNodes[nodes[0]];
11135 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11137 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11138 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11139 if ( vn0[i0] == vn1[i1] )
11140 doms.push_back( vn0[ i0 ]);
11141 if ( doms.size() > 2 )
11143 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11144 double *coords = grid->GetPoint(nodes[0]);
11145 gp_Pnt p0(coords[0], coords[1], coords[2]);
11146 coords = grid->GetPoint(nodes[nbNodes - 1]);
11147 gp_Pnt p1(coords[0], coords[1], coords[2]);
11149 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11150 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11151 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11152 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11153 for ( size_t id = 0; id < doms.size(); id++ )
11155 int idom = doms[id];
11156 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11157 for ( int ivol = 0; ivol < nbvol; ivol++ )
11159 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11160 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11161 if (domain.count(elem))
11163 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11164 domvol[idom] = svol;
11165 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11167 vtkIdType npts = 0;
11168 vtkIdType* pts = 0;
11169 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11170 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11173 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11174 angleDom[idom] = 0;
11178 gp_Pnt g(values[0], values[1], values[2]);
11179 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11180 //MESSAGE(" angle=" << angleDom[idom]);
11186 map<double, int> sortedDom; // sort domains by angle
11187 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11188 sortedDom[ia->second] = ia->first;
11189 vector<int> vnodes;
11191 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11193 vdom.push_back(ib->second);
11194 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11196 for (int ino = 0; ino < nbNodes; ino++)
11197 vnodes.push_back(nodes[ino]);
11198 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11207 // --- iterate on shared faces (volumes to modify, face to extrude)
11208 // get node id's of the face (id SMDS = id VTK)
11209 // create flat element with old and new nodes if requested
11211 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11212 // (domain1 X domain2) = domain1 + MAXINT*domain2
11214 std::map<int, std::map<long,int> > nodeQuadDomains;
11215 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11217 MESSAGE(".. Creation of elements: simple junction");
11218 if (createJointElems)
11221 string joints2DName = "joints2D";
11222 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11223 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11224 string joints3DName = "joints3D";
11225 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11226 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11228 itface = faceDomains.begin();
11229 for (; itface != faceDomains.end(); ++itface)
11231 DownIdType face = itface->first;
11232 std::set<int> oldNodes;
11233 std::set<int>::iterator itn;
11235 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237 std::map<int, int> domvol = itface->second;
11238 std::map<int, int>::iterator itdom = domvol.begin();
11239 int dom1 = itdom->first;
11240 int vtkVolId = itdom->second;
11242 int dom2 = itdom->first;
11243 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11245 stringstream grpname;
11248 grpname << dom1 << "_" << dom2;
11250 grpname << dom2 << "_" << dom1;
11251 string namegrp = grpname.str();
11252 if (!mapOfJunctionGroups.count(namegrp))
11253 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11254 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11256 sgrp->Add(vol->GetID());
11257 if (vol->GetType() == SMDSAbs_Volume)
11258 joints3DGrp->Add(vol->GetID());
11259 else if (vol->GetType() == SMDSAbs_Face)
11260 joints2DGrp->Add(vol->GetID());
11264 // --- create volumes on multiple domain intersection if requested
11265 // iterate on mutipleNodesToFace
11266 // iterate on edgesMultiDomains
11268 MESSAGE(".. Creation of elements: multiple junction");
11269 if (createJointElems)
11271 // --- iterate on mutipleNodesToFace
11273 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11274 for (; itn != mutipleNodesToFace.end(); ++itn)
11276 int node = itn->first;
11277 vector<int> orderDom = itn->second;
11278 vector<vtkIdType> orderedNodes;
11279 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11280 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11281 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11283 stringstream grpname;
11285 grpname << 0 << "_" << 0;
11287 string namegrp = grpname.str();
11288 if (!mapOfJunctionGroups.count(namegrp))
11289 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11290 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11292 sgrp->Add(face->GetID());
11295 // --- iterate on edgesMultiDomains
11297 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11298 for (; ite != edgesMultiDomains.end(); ++ite)
11300 vector<int> nodes = ite->first;
11301 vector<int> orderDom = ite->second;
11302 vector<vtkIdType> orderedNodes;
11303 if (nodes.size() == 2)
11305 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11306 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11307 if ( orderDom.size() == 3 )
11308 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11309 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11311 for (int idom = orderDom.size()-1; idom >=0; idom--)
11312 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11313 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11316 string namegrp = "jointsMultiples";
11317 if (!mapOfJunctionGroups.count(namegrp))
11318 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11319 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11321 sgrp->Add(vol->GetID());
11325 //INFOS("Quadratic multiple joints not implemented");
11326 // TODO quadratic nodes
11331 // --- list the explicit faces and edges of the mesh that need to be modified,
11332 // i.e. faces and edges built with one or more duplicated nodes.
11333 // associate these faces or edges to their corresponding domain.
11334 // only the first domain found is kept when a face or edge is shared
11336 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11337 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11338 faceOrEdgeDom.clear();
11341 MESSAGE(".. Modification of elements");
11342 for (int idomain = idom0; idomain < nbDomains; idomain++)
11344 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11345 for (; itnod != nodeDomains.end(); ++itnod)
11347 int oldId = itnod->first;
11348 //MESSAGE(" node " << oldId);
11349 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11350 for (int i = 0; i < l.ncells; i++)
11352 int vtkId = l.cells[i];
11353 int vtkType = grid->GetCellType(vtkId);
11354 int downId = grid->CellIdToDownId(vtkId);
11356 continue; // new cells: not to be modified
11357 DownIdType aCell(downId, vtkType);
11358 int volParents[1000];
11359 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11360 for (int j = 0; j < nbvol; j++)
11361 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11362 if (!feDom.count(vtkId))
11364 feDom[vtkId] = idomain;
11365 faceOrEdgeDom[aCell] = emptyMap;
11366 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11367 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11368 // << " type " << vtkType << " downId " << downId);
11374 // --- iterate on shared faces (volumes to modify, face to extrude)
11375 // get node id's of the face
11376 // replace old nodes by new nodes in volumes, and update inverse connectivity
11378 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11379 for (int m=0; m<3; m++)
11381 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11382 itface = (*amap).begin();
11383 for (; itface != (*amap).end(); ++itface)
11385 DownIdType face = itface->first;
11386 std::set<int> oldNodes;
11387 std::set<int>::iterator itn;
11389 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11390 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11391 std::map<int, int> localClonedNodeIds;
11393 std::map<int, int> domvol = itface->second;
11394 std::map<int, int>::iterator itdom = domvol.begin();
11395 for (; itdom != domvol.end(); ++itdom)
11397 int idom = itdom->first;
11398 int vtkVolId = itdom->second;
11399 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11400 localClonedNodeIds.clear();
11401 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11404 if (nodeDomains[oldId].count(idom))
11406 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11407 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11410 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11415 // Remove empty groups (issue 0022812)
11416 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11417 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11419 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11420 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11423 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11424 grid->DeleteLinks();
11432 * \brief Double nodes on some external faces and create flat elements.
11433 * Flat elements are mainly used by some types of mechanic calculations.
11435 * Each group of the list must be constituted of faces.
11436 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11437 * @param theElems - list of groups of faces, where a group of faces is a set of
11438 * SMDS_MeshElements sorted by Id.
11439 * @return TRUE if operation has been completed successfully, FALSE otherwise
11441 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11443 MESSAGE("-------------------------------------------------");
11444 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11445 MESSAGE("-------------------------------------------------");
11447 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11449 // --- For each group of faces
11450 // duplicate the nodes, create a flat element based on the face
11451 // replace the nodes of the faces by their clones
11453 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11454 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11455 clonedNodes.clear();
11456 intermediateNodes.clear();
11457 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11458 mapOfJunctionGroups.clear();
11460 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11462 const TIDSortedElemSet& domain = theElems[idom];
11463 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11464 for ( ; elemItr != domain.end(); ++elemItr )
11466 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11467 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11470 // MESSAGE("aFace=" << aFace->GetID());
11471 bool isQuad = aFace->IsQuadratic();
11472 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11474 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11476 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11477 while (nodeIt->more())
11479 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11480 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11482 ln2.push_back(node);
11484 ln0.push_back(node);
11486 const SMDS_MeshNode* clone = 0;
11487 if (!clonedNodes.count(node))
11489 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11490 copyPosition( node, clone );
11491 clonedNodes[node] = clone;
11494 clone = clonedNodes[node];
11497 ln3.push_back(clone);
11499 ln1.push_back(clone);
11501 const SMDS_MeshNode* inter = 0;
11502 if (isQuad && (!isMedium))
11504 if (!intermediateNodes.count(node))
11506 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11507 copyPosition( node, inter );
11508 intermediateNodes[node] = inter;
11511 inter = intermediateNodes[node];
11512 ln4.push_back(inter);
11516 // --- extrude the face
11518 vector<const SMDS_MeshNode*> ln;
11519 SMDS_MeshVolume* vol = 0;
11520 vtkIdType aType = aFace->GetVtkType();
11524 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11525 // MESSAGE("vol prism " << vol->GetID());
11526 ln.push_back(ln1[0]);
11527 ln.push_back(ln1[1]);
11528 ln.push_back(ln1[2]);
11531 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11532 // MESSAGE("vol hexa " << vol->GetID());
11533 ln.push_back(ln1[0]);
11534 ln.push_back(ln1[1]);
11535 ln.push_back(ln1[2]);
11536 ln.push_back(ln1[3]);
11538 case VTK_QUADRATIC_TRIANGLE:
11539 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11540 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11541 // MESSAGE("vol quad prism " << vol->GetID());
11542 ln.push_back(ln1[0]);
11543 ln.push_back(ln1[1]);
11544 ln.push_back(ln1[2]);
11545 ln.push_back(ln3[0]);
11546 ln.push_back(ln3[1]);
11547 ln.push_back(ln3[2]);
11549 case VTK_QUADRATIC_QUAD:
11550 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11551 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11552 // ln4[0], ln4[1], ln4[2], ln4[3]);
11553 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11554 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11555 ln4[0], ln4[1], ln4[2], ln4[3]);
11556 // MESSAGE("vol quad hexa " << vol->GetID());
11557 ln.push_back(ln1[0]);
11558 ln.push_back(ln1[1]);
11559 ln.push_back(ln1[2]);
11560 ln.push_back(ln1[3]);
11561 ln.push_back(ln3[0]);
11562 ln.push_back(ln3[1]);
11563 ln.push_back(ln3[2]);
11564 ln.push_back(ln3[3]);
11574 stringstream grpname;
11578 string namegrp = grpname.str();
11579 if (!mapOfJunctionGroups.count(namegrp))
11580 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11581 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11583 sgrp->Add(vol->GetID());
11586 // --- modify the face
11588 aFace->ChangeNodes(&ln[0], ln.size());
11595 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11596 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11597 * groups of faces to remove inside the object, (idem edges).
11598 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11600 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11601 const TopoDS_Shape& theShape,
11602 SMESH_NodeSearcher* theNodeSearcher,
11603 const char* groupName,
11604 std::vector<double>& nodesCoords,
11605 std::vector<std::vector<int> >& listOfListOfNodes)
11607 MESSAGE("--------------------------------");
11608 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11609 MESSAGE("--------------------------------");
11611 // --- zone of volumes to remove is given :
11612 // 1 either by a geom shape (one or more vertices) and a radius,
11613 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11614 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11615 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11616 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11617 // defined by it's name.
11619 SMESHDS_GroupBase* groupDS = 0;
11620 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11621 while ( groupIt->more() )
11624 SMESH_Group * group = groupIt->next();
11625 if ( !group ) continue;
11626 groupDS = group->GetGroupDS();
11627 if ( !groupDS || groupDS->IsEmpty() ) continue;
11628 std::string grpName = group->GetName();
11629 //MESSAGE("grpName=" << grpName);
11630 if (grpName == groupName)
11636 bool isNodeGroup = false;
11637 bool isNodeCoords = false;
11640 if (groupDS->GetType() != SMDSAbs_Node)
11642 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11645 if (nodesCoords.size() > 0)
11646 isNodeCoords = true; // a list o nodes given by their coordinates
11647 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11649 // --- define groups to build
11651 int idg; // --- group of SMDS volumes
11652 string grpvName = groupName;
11653 grpvName += "_vol";
11654 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11657 MESSAGE("group not created " << grpvName);
11660 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11662 int idgs; // --- group of SMDS faces on the skin
11663 string grpsName = groupName;
11664 grpsName += "_skin";
11665 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11668 MESSAGE("group not created " << grpsName);
11671 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11673 int idgi; // --- group of SMDS faces internal (several shapes)
11674 string grpiName = groupName;
11675 grpiName += "_internalFaces";
11676 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11679 MESSAGE("group not created " << grpiName);
11682 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11684 int idgei; // --- group of SMDS faces internal (several shapes)
11685 string grpeiName = groupName;
11686 grpeiName += "_internalEdges";
11687 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11690 MESSAGE("group not created " << grpeiName);
11693 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11695 // --- build downward connectivity
11697 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11698 meshDS->BuildDownWardConnectivity(true);
11699 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11701 // --- set of volumes detected inside
11703 std::set<int> setOfInsideVol;
11704 std::set<int> setOfVolToCheck;
11706 std::vector<gp_Pnt> gpnts;
11709 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11711 MESSAGE("group of nodes provided");
11712 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11713 while ( elemIt->more() )
11715 const SMDS_MeshElement* elem = elemIt->next();
11718 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11721 SMDS_MeshElement* vol = 0;
11722 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11723 while (volItr->more())
11725 vol = (SMDS_MeshElement*)volItr->next();
11726 setOfInsideVol.insert(vol->getVtkId());
11727 sgrp->Add(vol->GetID());
11731 else if (isNodeCoords)
11733 MESSAGE("list of nodes coordinates provided");
11736 while ( i < nodesCoords.size()-2 )
11738 double x = nodesCoords[i++];
11739 double y = nodesCoords[i++];
11740 double z = nodesCoords[i++];
11741 gp_Pnt p = gp_Pnt(x, y ,z);
11742 gpnts.push_back(p);
11743 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11747 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11749 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11750 TopTools_IndexedMapOfShape vertexMap;
11751 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11752 gp_Pnt p = gp_Pnt(0,0,0);
11753 if (vertexMap.Extent() < 1)
11756 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11758 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11759 p = BRep_Tool::Pnt(vertex);
11760 gpnts.push_back(p);
11761 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11765 if (gpnts.size() > 0)
11768 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11770 nodeId = startNode->GetID();
11771 MESSAGE("nodeId " << nodeId);
11773 double radius2 = radius*radius;
11774 MESSAGE("radius2 " << radius2);
11776 // --- volumes on start node
11778 setOfVolToCheck.clear();
11779 SMDS_MeshElement* startVol = 0;
11780 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11781 while (volItr->more())
11783 startVol = (SMDS_MeshElement*)volItr->next();
11784 setOfVolToCheck.insert(startVol->getVtkId());
11786 if (setOfVolToCheck.empty())
11788 MESSAGE("No volumes found");
11792 // --- starting with central volumes then their neighbors, check if they are inside
11793 // or outside the domain, until no more new neighbor volume is inside.
11794 // Fill the group of inside volumes
11796 std::map<int, double> mapOfNodeDistance2;
11797 mapOfNodeDistance2.clear();
11798 std::set<int> setOfOutsideVol;
11799 while (!setOfVolToCheck.empty())
11801 std::set<int>::iterator it = setOfVolToCheck.begin();
11803 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11804 bool volInside = false;
11805 vtkIdType npts = 0;
11806 vtkIdType* pts = 0;
11807 grid->GetCellPoints(vtkId, npts, pts);
11808 for (int i=0; i<npts; i++)
11810 double distance2 = 0;
11811 if (mapOfNodeDistance2.count(pts[i]))
11813 distance2 = mapOfNodeDistance2[pts[i]];
11814 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11818 double *coords = grid->GetPoint(pts[i]);
11819 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11821 for ( size_t j = 0; j < gpnts.size(); j++ )
11823 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11824 if (d2 < distance2)
11827 if (distance2 < radius2)
11831 mapOfNodeDistance2[pts[i]] = distance2;
11832 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11834 if (distance2 < radius2)
11836 volInside = true; // one or more nodes inside the domain
11837 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11843 setOfInsideVol.insert(vtkId);
11844 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11845 int neighborsVtkIds[NBMAXNEIGHBORS];
11846 int downIds[NBMAXNEIGHBORS];
11847 unsigned char downTypes[NBMAXNEIGHBORS];
11848 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11849 for (int n = 0; n < nbNeighbors; n++)
11850 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11851 setOfVolToCheck.insert(neighborsVtkIds[n]);
11855 setOfOutsideVol.insert(vtkId);
11856 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11858 setOfVolToCheck.erase(vtkId);
11862 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11863 // If yes, add the volume to the inside set
11865 bool addedInside = true;
11866 std::set<int> setOfVolToReCheck;
11867 while (addedInside)
11869 MESSAGE(" --------------------------- re check");
11870 addedInside = false;
11871 std::set<int>::iterator itv = setOfInsideVol.begin();
11872 for (; itv != setOfInsideVol.end(); ++itv)
11875 int neighborsVtkIds[NBMAXNEIGHBORS];
11876 int downIds[NBMAXNEIGHBORS];
11877 unsigned char downTypes[NBMAXNEIGHBORS];
11878 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11879 for (int n = 0; n < nbNeighbors; n++)
11880 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11881 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11883 setOfVolToCheck = setOfVolToReCheck;
11884 setOfVolToReCheck.clear();
11885 while (!setOfVolToCheck.empty())
11887 std::set<int>::iterator it = setOfVolToCheck.begin();
11889 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11891 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11892 int countInside = 0;
11893 int neighborsVtkIds[NBMAXNEIGHBORS];
11894 int downIds[NBMAXNEIGHBORS];
11895 unsigned char downTypes[NBMAXNEIGHBORS];
11896 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11897 for (int n = 0; n < nbNeighbors; n++)
11898 if (setOfInsideVol.count(neighborsVtkIds[n]))
11900 MESSAGE("countInside " << countInside);
11901 if (countInside > 1)
11903 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11904 setOfInsideVol.insert(vtkId);
11905 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11906 addedInside = true;
11909 setOfVolToReCheck.insert(vtkId);
11911 setOfVolToCheck.erase(vtkId);
11915 // --- map of Downward faces at the boundary, inside the global volume
11916 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11917 // fill group of SMDS faces inside the volume (when several volume shapes)
11918 // fill group of SMDS faces on the skin of the global volume (if skin)
11920 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11921 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11922 std::set<int>::iterator it = setOfInsideVol.begin();
11923 for (; it != setOfInsideVol.end(); ++it)
11926 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11927 int neighborsVtkIds[NBMAXNEIGHBORS];
11928 int downIds[NBMAXNEIGHBORS];
11929 unsigned char downTypes[NBMAXNEIGHBORS];
11930 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11931 for (int n = 0; n < nbNeighbors; n++)
11933 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11934 if (neighborDim == 3)
11936 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11938 DownIdType face(downIds[n], downTypes[n]);
11939 boundaryFaces[face] = vtkId;
11941 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11942 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11943 if (vtkFaceId >= 0)
11945 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11946 // find also the smds edges on this face
11947 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11948 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11949 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11950 for (int i = 0; i < nbEdges; i++)
11952 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11953 if (vtkEdgeId >= 0)
11954 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11958 else if (neighborDim == 2) // skin of the volume
11960 DownIdType face(downIds[n], downTypes[n]);
11961 skinFaces[face] = vtkId;
11962 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11963 if (vtkFaceId >= 0)
11964 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11969 // --- identify the edges constituting the wire of each subshape on the skin
11970 // define polylines with the nodes of edges, equivalent to wires
11971 // project polylines on subshapes, and partition, to get geom faces
11973 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11974 std::set<int> emptySet;
11976 std::set<int> shapeIds;
11978 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11979 while (itelem->more())
11981 const SMDS_MeshElement *elem = itelem->next();
11982 int shapeId = elem->getshapeId();
11983 int vtkId = elem->getVtkId();
11984 if (!shapeIdToVtkIdSet.count(shapeId))
11986 shapeIdToVtkIdSet[shapeId] = emptySet;
11987 shapeIds.insert(shapeId);
11989 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11992 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11993 std::set<DownIdType, DownIdCompare> emptyEdges;
11994 emptyEdges.clear();
11996 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11997 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11999 int shapeId = itShape->first;
12000 MESSAGE(" --- Shape ID --- "<< shapeId);
12001 shapeIdToEdges[shapeId] = emptyEdges;
12003 std::vector<int> nodesEdges;
12005 std::set<int>::iterator its = itShape->second.begin();
12006 for (; its != itShape->second.end(); ++its)
12009 MESSAGE(" " << vtkId);
12010 int neighborsVtkIds[NBMAXNEIGHBORS];
12011 int downIds[NBMAXNEIGHBORS];
12012 unsigned char downTypes[NBMAXNEIGHBORS];
12013 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12014 for (int n = 0; n < nbNeighbors; n++)
12016 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12018 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12019 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12020 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12022 DownIdType edge(downIds[n], downTypes[n]);
12023 if (!shapeIdToEdges[shapeId].count(edge))
12025 shapeIdToEdges[shapeId].insert(edge);
12027 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12028 nodesEdges.push_back(vtkNodeId[0]);
12029 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12030 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12036 std::list<int> order;
12038 if (nodesEdges.size() > 0)
12040 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12041 nodesEdges[0] = -1;
12042 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12043 nodesEdges[1] = -1; // do not reuse this edge
12047 int nodeTofind = order.back(); // try first to push back
12049 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12050 if (nodesEdges[i] == nodeTofind)
12052 if ( i == (int) nodesEdges.size() )
12053 found = false; // no follower found on back
12056 if (i%2) // odd ==> use the previous one
12057 if (nodesEdges[i-1] < 0)
12061 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12062 nodesEdges[i-1] = -1;
12064 else // even ==> use the next one
12065 if (nodesEdges[i+1] < 0)
12069 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12070 nodesEdges[i+1] = -1;
12075 // try to push front
12077 nodeTofind = order.front(); // try to push front
12078 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12079 if ( nodesEdges[i] == nodeTofind )
12081 if ( i == (int)nodesEdges.size() )
12083 found = false; // no predecessor found on front
12086 if (i%2) // odd ==> use the previous one
12087 if (nodesEdges[i-1] < 0)
12091 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12092 nodesEdges[i-1] = -1;
12094 else // even ==> use the next one
12095 if (nodesEdges[i+1] < 0)
12099 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12100 nodesEdges[i+1] = -1;
12106 std::vector<int> nodes;
12107 nodes.push_back(shapeId);
12108 std::list<int>::iterator itl = order.begin();
12109 for (; itl != order.end(); itl++)
12111 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12112 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12114 listOfListOfNodes.push_back(nodes);
12117 // partition geom faces with blocFissure
12118 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12119 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12125 //================================================================================
12127 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12128 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12129 * \return TRUE if operation has been completed successfully, FALSE otherwise
12131 //================================================================================
12133 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12135 // iterates on volume elements and detect all free faces on them
12136 SMESHDS_Mesh* aMesh = GetMeshDS();
12140 ElemFeatures faceType( SMDSAbs_Face );
12141 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12142 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12145 const SMDS_MeshVolume* volume = vIt->next();
12146 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12147 vTool.SetExternalNormal();
12148 const int iQuad = volume->IsQuadratic();
12149 faceType.SetQuad( iQuad );
12150 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12152 if (!vTool.IsFreeFace(iface))
12155 vector<const SMDS_MeshNode *> nodes;
12156 int nbFaceNodes = vTool.NbFaceNodes(iface);
12157 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12159 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12160 nodes.push_back(faceNodes[inode]);
12162 if (iQuad) // add medium nodes
12164 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12165 nodes.push_back(faceNodes[inode]);
12166 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12167 nodes.push_back(faceNodes[8]);
12169 // add new face based on volume nodes
12170 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12172 nbExisted++; // face already exsist
12176 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12181 return ( nbFree == ( nbExisted + nbCreated ));
12186 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12188 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12190 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12193 //================================================================================
12195 * \brief Creates missing boundary elements
12196 * \param elements - elements whose boundary is to be checked
12197 * \param dimension - defines type of boundary elements to create
12198 * \param group - a group to store created boundary elements in
12199 * \param targetMesh - a mesh to store created boundary elements in
12200 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12201 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12202 * boundary elements will be copied into the targetMesh
12203 * \param toAddExistingBondary - if true, not only new but also pre-existing
12204 * boundary elements will be added into the new group
12205 * \param aroundElements - if true, elements will be created on boundary of given
12206 * elements else, on boundary of the whole mesh.
12207 * \return nb of added boundary elements
12209 //================================================================================
12211 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12212 Bnd_Dimension dimension,
12213 SMESH_Group* group/*=0*/,
12214 SMESH_Mesh* targetMesh/*=0*/,
12215 bool toCopyElements/*=false*/,
12216 bool toCopyExistingBoundary/*=false*/,
12217 bool toAddExistingBondary/*= false*/,
12218 bool aroundElements/*= false*/)
12220 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12221 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12222 // hope that all elements are of the same type, do not check them all
12223 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12224 throw SALOME_Exception(LOCALIZED("wrong element type"));
12227 toCopyElements = toCopyExistingBoundary = false;
12229 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12230 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12231 int nbAddedBnd = 0;
12233 // editor adding present bnd elements and optionally holding elements to add to the group
12234 SMESH_MeshEditor* presentEditor;
12235 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12236 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12238 SMESH_MesherHelper helper( *myMesh );
12239 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12240 SMDS_VolumeTool vTool;
12241 TIDSortedElemSet avoidSet;
12242 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12245 typedef vector<const SMDS_MeshNode*> TConnectivity;
12246 TConnectivity tgtNodes;
12247 ElemFeatures elemKind( missType ), elemToCopy;
12249 vector<const SMDS_MeshElement*> presentBndElems;
12250 vector<TConnectivity> missingBndElems;
12251 vector<int> freeFacets;
12252 TConnectivity nodes, elemNodes;
12254 SMDS_ElemIteratorPtr eIt;
12255 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12256 else eIt = SMESHUtils::elemSetIterator( elements );
12258 while (eIt->more())
12260 const SMDS_MeshElement* elem = eIt->next();
12261 const int iQuad = elem->IsQuadratic();
12262 elemKind.SetQuad( iQuad );
12264 // ------------------------------------------------------------------------------------
12265 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12266 // ------------------------------------------------------------------------------------
12267 presentBndElems.clear();
12268 missingBndElems.clear();
12269 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12270 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12272 const SMDS_MeshElement* otherVol = 0;
12273 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12275 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12276 ( !aroundElements || elements.count( otherVol )))
12278 freeFacets.push_back( iface );
12280 if ( missType == SMDSAbs_Face )
12281 vTool.SetExternalNormal();
12282 for ( size_t i = 0; i < freeFacets.size(); ++i )
12284 int iface = freeFacets[i];
12285 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12286 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12287 if ( missType == SMDSAbs_Edge ) // boundary edges
12289 nodes.resize( 2+iQuad );
12290 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12292 for ( size_t j = 0; j < nodes.size(); ++j )
12293 nodes[ j ] = nn[ i+j ];
12294 if ( const SMDS_MeshElement* edge =
12295 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12296 presentBndElems.push_back( edge );
12298 missingBndElems.push_back( nodes );
12301 else // boundary face
12304 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12305 nodes.push_back( nn[inode] ); // add corner nodes
12307 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12308 nodes.push_back( nn[inode] ); // add medium nodes
12309 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12311 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12313 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12314 SMDSAbs_Face, /*noMedium=*/false ))
12315 presentBndElems.push_back( f );
12317 missingBndElems.push_back( nodes );
12319 if ( targetMesh != myMesh )
12321 // add 1D elements on face boundary to be added to a new mesh
12322 const SMDS_MeshElement* edge;
12323 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12326 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12328 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12329 if ( edge && avoidSet.insert( edge ).second )
12330 presentBndElems.push_back( edge );
12336 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12338 avoidSet.clear(), avoidSet.insert( elem );
12339 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12340 SMDS_MeshElement::iterator() );
12341 elemNodes.push_back( elemNodes[0] );
12342 nodes.resize( 2 + iQuad );
12343 const int nbLinks = elem->NbCornerNodes();
12344 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12346 nodes[0] = elemNodes[iN];
12347 nodes[1] = elemNodes[iN+1+iQuad];
12348 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12349 continue; // not free link
12351 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12352 if ( const SMDS_MeshElement* edge =
12353 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12354 presentBndElems.push_back( edge );
12356 missingBndElems.push_back( nodes );
12360 // ---------------------------------
12361 // 2. Add missing boundary elements
12362 // ---------------------------------
12363 if ( targetMesh != myMesh )
12364 // instead of making a map of nodes in this mesh and targetMesh,
12365 // we create nodes with same IDs.
12366 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12368 TConnectivity& srcNodes = missingBndElems[i];
12369 tgtNodes.resize( srcNodes.size() );
12370 for ( inode = 0; inode < srcNodes.size(); ++inode )
12371 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12372 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12374 /*noMedium=*/false))
12376 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12380 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12382 TConnectivity& nodes = missingBndElems[ i ];
12383 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12385 /*noMedium=*/false))
12387 SMDS_MeshElement* newElem =
12388 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12389 nbAddedBnd += bool( newElem );
12391 // try to set a new element to a shape
12392 if ( myMesh->HasShapeToMesh() )
12395 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12396 const size_t nbN = nodes.size() / (iQuad+1 );
12397 for ( inode = 0; inode < nbN && ok; ++inode )
12399 pair<int, TopAbs_ShapeEnum> i_stype =
12400 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12401 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12402 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12404 if ( ok && mediumShapes.size() > 1 )
12406 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12407 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12408 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12410 if (( ok = ( stype_i->first != stype_i_0.first )))
12411 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12412 aMesh->IndexToShape( stype_i_0.second ));
12415 if ( ok && mediumShapes.begin()->first == missShapeType )
12416 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12420 // ----------------------------------
12421 // 3. Copy present boundary elements
12422 // ----------------------------------
12423 if ( toCopyExistingBoundary )
12424 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12426 const SMDS_MeshElement* e = presentBndElems[i];
12427 tgtNodes.resize( e->NbNodes() );
12428 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12429 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12430 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12432 else // store present elements to add them to a group
12433 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12435 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12438 } // loop on given elements
12440 // ---------------------------------------------
12441 // 4. Fill group with boundary elements
12442 // ---------------------------------------------
12445 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12446 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12447 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12449 tgtEditor.myLastCreatedElems.clear();
12450 tgtEditor2.myLastCreatedElems.clear();
12452 // -----------------------
12453 // 5. Copy given elements
12454 // -----------------------
12455 if ( toCopyElements && targetMesh != myMesh )
12457 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12458 else eIt = SMESHUtils::elemSetIterator( elements );
12459 while (eIt->more())
12461 const SMDS_MeshElement* elem = eIt->next();
12462 tgtNodes.resize( elem->NbNodes() );
12463 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12464 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12465 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12467 tgtEditor.myLastCreatedElems.clear();
12473 //================================================================================
12475 * \brief Copy node position and set \a to node on the same geometry
12477 //================================================================================
12479 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12480 const SMDS_MeshNode* to )
12482 if ( !from || !to ) return;
12484 SMDS_PositionPtr pos = from->GetPosition();
12485 if ( !pos || from->getshapeId() < 1 ) return;
12487 switch ( pos->GetTypeOfPosition() )
12489 case SMDS_TOP_3DSPACE: break;
12491 case SMDS_TOP_FACE:
12493 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12494 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12495 fPos->GetUParameter(), fPos->GetVParameter() );
12498 case SMDS_TOP_EDGE:
12500 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12501 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12502 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12505 case SMDS_TOP_VERTEX:
12507 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12510 case SMDS_TOP_UNSPEC: